| Conditions | 1 |
| Paths | 0 |
| Total Lines | 11678 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 2 | ||
| Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | ;var MXI_DEBUG = true; |
||
| 36 | (function(exports, undefined) { |
||
| 37 | "use strict"; |
||
| 38 | |||
| 39 | var modules = {}; |
||
| 40 | |||
| 41 | function require(ids, callback) { |
||
| 42 | var module, defs = []; |
||
| 43 | |||
| 44 | for (var i = 0; i < ids.length; ++i) { |
||
| 45 | module = modules[ids[i]] || resolve(ids[i]); |
||
| 46 | if (!module) { |
||
| 47 | throw 'module definition dependecy not found: ' + ids[i]; |
||
| 48 | } |
||
| 49 | |||
| 50 | defs.push(module); |
||
| 51 | } |
||
| 52 | |||
| 53 | callback.apply(null, defs); |
||
| 54 | } |
||
| 55 | |||
| 56 | function define(id, dependencies, definition) { |
||
| 57 | if (typeof id !== 'string') { |
||
| 58 | throw 'invalid module definition, module id must be defined and be a string'; |
||
| 59 | } |
||
| 60 | |||
| 61 | if (dependencies === undefined) { |
||
| 62 | throw 'invalid module definition, dependencies must be specified'; |
||
| 63 | } |
||
| 64 | |||
| 65 | if (definition === undefined) { |
||
| 66 | throw 'invalid module definition, definition function must be specified'; |
||
| 67 | } |
||
| 68 | |||
| 69 | require(dependencies, function() { |
||
| 70 | modules[id] = definition.apply(null, arguments); |
||
| 71 | }); |
||
| 72 | } |
||
| 73 | |||
| 74 | function defined(id) { |
||
| 75 | return !!modules[id]; |
||
| 76 | } |
||
| 77 | |||
| 78 | function resolve(id) { |
||
| 79 | var target = exports; |
||
| 80 | var fragments = id.split(/[.\/]/); |
||
| 81 | |||
| 82 | for (var fi = 0; fi < fragments.length; ++fi) { |
||
| 83 | if (!target[fragments[fi]]) { |
||
| 84 | return; |
||
| 85 | } |
||
| 86 | |||
| 87 | target = target[fragments[fi]]; |
||
| 88 | } |
||
| 89 | |||
| 90 | return target; |
||
| 91 | } |
||
| 92 | |||
| 93 | function expose(ids) { |
||
| 94 | for (var i = 0; i < ids.length; i++) { |
||
| 95 | var target = exports; |
||
| 96 | var id = ids[i]; |
||
| 97 | var fragments = id.split(/[.\/]/); |
||
| 98 | |||
| 99 | for (var fi = 0; fi < fragments.length - 1; ++fi) { |
||
| 100 | if (target[fragments[fi]] === undefined) { |
||
| 101 | target[fragments[fi]] = {}; |
||
| 102 | } |
||
| 103 | |||
| 104 | target = target[fragments[fi]]; |
||
| 105 | } |
||
| 106 | |||
| 107 | target[fragments[fragments.length - 1]] = modules[id]; |
||
| 108 | } |
||
| 109 | } |
||
| 110 | |||
| 111 | // Included from: src/javascript/core/utils/Basic.js |
||
| 112 | |||
| 113 | /** |
||
| 114 | * Basic.js |
||
| 115 | * |
||
| 116 | * Copyright 2013, Moxiecode Systems AB |
||
| 117 | * Released under GPL License. |
||
| 118 | * |
||
| 119 | * License: http://www.plupload.com/license |
||
| 120 | * Contributing: http://www.plupload.com/contributing |
||
| 121 | */ |
||
| 122 | |||
| 123 | /** |
||
| 124 | @class moxie/core/utils/Basic |
||
| 125 | @public |
||
| 126 | @static |
||
| 127 | */ |
||
| 128 | |||
| 129 | define('moxie/core/utils/Basic', [], function() { |
||
| 130 | /** |
||
| 131 | Gets the true type of the built-in object (better version of typeof). |
||
| 132 | @author Angus Croll (http://javascriptweblog.wordpress.com/) |
||
| 133 | |||
| 134 | @method typeOf |
||
| 135 | @static |
||
| 136 | @param {Object} o Object to check. |
||
| 137 | @return {String} Object [[Class]] |
||
| 138 | */ |
||
| 139 | function typeOf(o) { |
||
| 140 | var undef; |
||
| 141 | |||
| 142 | if (o === undef) { |
||
|
|
|||
| 143 | return 'undefined'; |
||
| 144 | } else if (o === null) { |
||
| 145 | return 'null'; |
||
| 146 | } else if (o.nodeType) { |
||
| 147 | return 'node'; |
||
| 148 | } |
||
| 149 | |||
| 150 | // the snippet below is awesome, however it fails to detect null, undefined and arguments types in IE lte 8 |
||
| 151 | return ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase(); |
||
| 152 | } |
||
| 153 | |||
| 154 | /** |
||
| 155 | Extends the specified object with another object(s). |
||
| 156 | |||
| 157 | @method extend |
||
| 158 | @static |
||
| 159 | @param {Object} target Object to extend. |
||
| 160 | @param {Object} [obj]* Multiple objects to extend with. |
||
| 161 | @return {Object} Same as target, the extended object. |
||
| 162 | */ |
||
| 163 | function extend() { |
||
| 164 | return merge(false, false, arguments); |
||
| 165 | } |
||
| 166 | |||
| 167 | |||
| 168 | /** |
||
| 169 | Extends the specified object with another object(s), but only if the property exists in the target. |
||
| 170 | |||
| 171 | @method extendIf |
||
| 172 | @static |
||
| 173 | @param {Object} target Object to extend. |
||
| 174 | @param {Object} [obj]* Multiple objects to extend with. |
||
| 175 | @return {Object} Same as target, the extended object. |
||
| 176 | */ |
||
| 177 | function extendIf() { |
||
| 178 | return merge(true, false, arguments); |
||
| 179 | } |
||
| 180 | |||
| 181 | |||
| 182 | function extendImmutable() { |
||
| 183 | return merge(false, true, arguments); |
||
| 184 | } |
||
| 185 | |||
| 186 | |||
| 187 | function extendImmutableIf() { |
||
| 188 | return merge(true, true, arguments); |
||
| 189 | } |
||
| 190 | |||
| 191 | |||
| 192 | function clone(value) { |
||
| 193 | switch (typeOf(value)) { |
||
| 194 | case 'array': |
||
| 195 | return merge(false, true, [[], value]); |
||
| 196 | |||
| 197 | case 'object': |
||
| 198 | return merge(false, true, [{}, value]); |
||
| 199 | |||
| 200 | default: |
||
| 201 | return value; |
||
| 202 | } |
||
| 203 | } |
||
| 204 | |||
| 205 | |||
| 206 | function shallowCopy(obj) { |
||
| 207 | switch (typeOf(obj)) { |
||
| 208 | case 'array': |
||
| 209 | return Array.prototype.slice.call(obj); |
||
| 210 | |||
| 211 | case 'object': |
||
| 212 | return extend({}, obj); |
||
| 213 | } |
||
| 214 | return obj; |
||
| 215 | } |
||
| 216 | |||
| 217 | |||
| 218 | function merge(strict, immutable, args) { |
||
| 219 | var undef; |
||
| 220 | var target = args[0]; |
||
| 221 | |||
| 222 | each(args, function(arg, i) { |
||
| 223 | if (i > 0) { |
||
| 224 | each(arg, function(value, key) { |
||
| 225 | var isComplex = inArray(typeOf(value), ['array', 'object']) !== -1; |
||
| 226 | |||
| 227 | if (value === undef || strict && target[key] === undef) { |
||
| 228 | return true; |
||
| 229 | } |
||
| 230 | |||
| 231 | if (isComplex && immutable) { |
||
| 232 | value = shallowCopy(value); |
||
| 233 | } |
||
| 234 | |||
| 235 | if (typeOf(target[key]) === typeOf(value) && isComplex) { |
||
| 236 | merge(strict, immutable, [target[key], value]); |
||
| 237 | } else { |
||
| 238 | target[key] = value; |
||
| 239 | } |
||
| 240 | }); |
||
| 241 | } |
||
| 242 | }); |
||
| 243 | |||
| 244 | return target; |
||
| 245 | } |
||
| 246 | |||
| 247 | |||
| 248 | /** |
||
| 249 | A way to inherit one `class` from another in a consisstent way (more or less) |
||
| 250 | |||
| 251 | @method inherit |
||
| 252 | @static |
||
| 253 | @since >1.4.1 |
||
| 254 | @param {Function} child |
||
| 255 | @param {Function} parent |
||
| 256 | @return {Function} Prepared constructor |
||
| 257 | */ |
||
| 258 | function inherit(child, parent) { |
||
| 259 | // copy over all parent properties |
||
| 260 | for (var key in parent) { |
||
| 261 | if ({}.hasOwnProperty.call(parent, key)) { |
||
| 262 | child[key] = parent[key]; |
||
| 263 | } |
||
| 264 | } |
||
| 265 | |||
| 266 | // give child `class` a place to define its own methods |
||
| 267 | function ctor() { |
||
| 268 | this.constructor = child; |
||
| 269 | |||
| 270 | if (MXI_DEBUG) { |
||
| 271 | var getCtorName = function(fn) { |
||
| 272 | var m = fn.toString().match(/^function\s([^\(\s]+)/); |
||
| 273 | return m ? m[1] : false; |
||
| 274 | }; |
||
| 275 | |||
| 276 | this.ctorName = getCtorName(child); |
||
| 277 | } |
||
| 278 | } |
||
| 279 | ctor.prototype = parent.prototype; |
||
| 280 | child.prototype = new ctor(); |
||
| 281 | |||
| 282 | // keep a way to reference parent methods |
||
| 283 | child.parent = parent.prototype; |
||
| 284 | return child; |
||
| 285 | } |
||
| 286 | |||
| 287 | |||
| 288 | /** |
||
| 289 | Executes the callback function for each item in array/object. If you return false in the |
||
| 290 | callback it will break the loop. |
||
| 291 | |||
| 292 | @method each |
||
| 293 | @static |
||
| 294 | @param {Object} obj Object to iterate. |
||
| 295 | @param {function} callback Callback function to execute for each item. |
||
| 296 | */ |
||
| 297 | function each(obj, callback) { |
||
| 298 | var length, key, i, undef; |
||
| 299 | |||
| 300 | if (obj) { |
||
| 301 | try { |
||
| 302 | length = obj.length; |
||
| 303 | } catch(ex) { |
||
| 304 | length = undef; |
||
| 305 | } |
||
| 306 | |||
| 307 | if (length === undef || typeof(length) !== 'number') { |
||
| 308 | // Loop object items |
||
| 309 | for (key in obj) { |
||
| 310 | if (obj.hasOwnProperty(key)) { |
||
| 311 | if (callback(obj[key], key) === false) { |
||
| 312 | return; |
||
| 313 | } |
||
| 314 | } |
||
| 315 | } |
||
| 316 | } else { |
||
| 317 | // Loop array items |
||
| 318 | for (i = 0; i < length; i++) { |
||
| 319 | if (callback(obj[i], i) === false) { |
||
| 320 | return; |
||
| 321 | } |
||
| 322 | } |
||
| 323 | } |
||
| 324 | } |
||
| 325 | } |
||
| 326 | |||
| 327 | /** |
||
| 328 | Checks if object is empty. |
||
| 329 | |||
| 330 | @method isEmptyObj |
||
| 331 | @static |
||
| 332 | @param {Object} o Object to check. |
||
| 333 | @return {Boolean} |
||
| 334 | */ |
||
| 335 | function isEmptyObj(obj) { |
||
| 336 | var prop; |
||
| 337 | |||
| 338 | if (!obj || typeOf(obj) !== 'object') { |
||
| 339 | return true; |
||
| 340 | } |
||
| 341 | |||
| 342 | for (prop in obj) { |
||
| 343 | return false; |
||
| 344 | } |
||
| 345 | |||
| 346 | return true; |
||
| 347 | } |
||
| 348 | |||
| 349 | /** |
||
| 350 | Recieve an array of functions (usually async) to call in sequence, each function |
||
| 351 | receives a callback as first argument that it should call, when it completes. Finally, |
||
| 352 | after everything is complete, main callback is called. Passing truthy value to the |
||
| 353 | callback as a first argument will interrupt the sequence and invoke main callback |
||
| 354 | immediately. |
||
| 355 | |||
| 356 | @method inSeries |
||
| 357 | @static |
||
| 358 | @param {Array} queue Array of functions to call in sequence |
||
| 359 | @param {Function} cb Main callback that is called in the end, or in case of error |
||
| 360 | */ |
||
| 361 | function inSeries(queue, cb) { |
||
| 362 | var i = 0, length = queue.length; |
||
| 363 | |||
| 364 | if (typeOf(cb) !== 'function') { |
||
| 365 | cb = function() {}; |
||
| 366 | } |
||
| 367 | |||
| 368 | if (!queue || !queue.length) { |
||
| 369 | cb(); |
||
| 370 | } |
||
| 371 | |||
| 372 | function callNext(i) { |
||
| 373 | if (typeOf(queue[i]) === 'function') { |
||
| 374 | queue[i](function(error) { |
||
| 375 | /*jshint expr:true */ |
||
| 376 | ++i < length && !error ? callNext(i) : cb(error); |
||
| 377 | }); |
||
| 378 | } |
||
| 379 | } |
||
| 380 | callNext(i); |
||
| 381 | } |
||
| 382 | |||
| 383 | |||
| 384 | /** |
||
| 385 | Recieve an array of functions (usually async) to call in parallel, each function |
||
| 386 | receives a callback as first argument that it should call, when it completes. After |
||
| 387 | everything is complete, main callback is called. Passing truthy value to the |
||
| 388 | callback as a first argument will interrupt the process and invoke main callback |
||
| 389 | immediately. |
||
| 390 | |||
| 391 | @method inParallel |
||
| 392 | @static |
||
| 393 | @param {Array} queue Array of functions to call in sequence |
||
| 394 | @param {Function} cb Main callback that is called in the end, or in case of erro |
||
| 395 | */ |
||
| 396 | function inParallel(queue, cb) { |
||
| 397 | var count = 0, num = queue.length, cbArgs = new Array(num); |
||
| 398 | |||
| 399 | each(queue, function(fn, i) { |
||
| 400 | fn(function(error) { |
||
| 401 | if (error) { |
||
| 402 | return cb(error); |
||
| 403 | } |
||
| 404 | |||
| 405 | var args = [].slice.call(arguments); |
||
| 406 | args.shift(); // strip error - undefined or not |
||
| 407 | |||
| 408 | cbArgs[i] = args; |
||
| 409 | count++; |
||
| 410 | |||
| 411 | if (count === num) { |
||
| 412 | cbArgs.unshift(null); |
||
| 413 | cb.apply(this, cbArgs); |
||
| 414 | } |
||
| 415 | }); |
||
| 416 | }); |
||
| 417 | } |
||
| 418 | |||
| 419 | |||
| 420 | /** |
||
| 421 | Find an element in array and return it's index if present, otherwise return -1. |
||
| 422 | |||
| 423 | @method inArray |
||
| 424 | @static |
||
| 425 | @param {Mixed} needle Element to find |
||
| 426 | @param {Array} array |
||
| 427 | @return {Int} Index of the element, or -1 if not found |
||
| 428 | */ |
||
| 429 | function inArray(needle, array) { |
||
| 430 | if (array) { |
||
| 431 | if (Array.prototype.indexOf) { |
||
| 432 | return Array.prototype.indexOf.call(array, needle); |
||
| 433 | } |
||
| 434 | |||
| 435 | for (var i = 0, length = array.length; i < length; i++) { |
||
| 436 | if (array[i] === needle) { |
||
| 437 | return i; |
||
| 438 | } |
||
| 439 | } |
||
| 440 | } |
||
| 441 | return -1; |
||
| 442 | } |
||
| 443 | |||
| 444 | |||
| 445 | /** |
||
| 446 | Returns elements of first array if they are not present in second. And false - otherwise. |
||
| 447 | |||
| 448 | @private |
||
| 449 | @method arrayDiff |
||
| 450 | @param {Array} needles |
||
| 451 | @param {Array} array |
||
| 452 | @return {Array|Boolean} |
||
| 453 | */ |
||
| 454 | function arrayDiff(needles, array) { |
||
| 455 | var diff = []; |
||
| 456 | |||
| 457 | if (typeOf(needles) !== 'array') { |
||
| 458 | needles = [needles]; |
||
| 459 | } |
||
| 460 | |||
| 461 | if (typeOf(array) !== 'array') { |
||
| 462 | array = [array]; |
||
| 463 | } |
||
| 464 | |||
| 465 | for (var i in needles) { |
||
| 466 | if (inArray(needles[i], array) === -1) { |
||
| 467 | diff.push(needles[i]); |
||
| 468 | } |
||
| 469 | } |
||
| 470 | return diff.length ? diff : false; |
||
| 471 | } |
||
| 472 | |||
| 473 | |||
| 474 | /** |
||
| 475 | Find intersection of two arrays. |
||
| 476 | |||
| 477 | @private |
||
| 478 | @method arrayIntersect |
||
| 479 | @param {Array} array1 |
||
| 480 | @param {Array} array2 |
||
| 481 | @return {Array} Intersection of two arrays or null if there is none |
||
| 482 | */ |
||
| 483 | function arrayIntersect(array1, array2) { |
||
| 484 | var result = []; |
||
| 485 | each(array1, function(item) { |
||
| 486 | if (inArray(item, array2) !== -1) { |
||
| 487 | result.push(item); |
||
| 488 | } |
||
| 489 | }); |
||
| 490 | return result.length ? result : null; |
||
| 491 | } |
||
| 492 | |||
| 493 | |||
| 494 | /** |
||
| 495 | Forces anything into an array. |
||
| 496 | |||
| 497 | @method toArray |
||
| 498 | @static |
||
| 499 | @param {Object} obj Object with length field. |
||
| 500 | @return {Array} Array object containing all items. |
||
| 501 | */ |
||
| 502 | function toArray(obj) { |
||
| 503 | var i, arr = []; |
||
| 504 | |||
| 505 | for (i = 0; i < obj.length; i++) { |
||
| 506 | arr[i] = obj[i]; |
||
| 507 | } |
||
| 508 | |||
| 509 | return arr; |
||
| 510 | } |
||
| 511 | |||
| 512 | |||
| 513 | /** |
||
| 514 | Generates an unique ID. The only way a user would be able to get the same ID is if the two persons |
||
| 515 | at the same exact millisecond manage to get the same 5 random numbers between 0-65535; it also uses |
||
| 516 | a counter so each ID is guaranteed to be unique for the given page. It is more probable for the earth |
||
| 517 | to be hit with an asteroid. |
||
| 518 | |||
| 519 | @method guid |
||
| 520 | @static |
||
| 521 | @param {String} prefix to prepend (by default 'o' will be prepended). |
||
| 522 | @method guid |
||
| 523 | @return {String} Virtually unique id. |
||
| 524 | */ |
||
| 525 | var guid = (function() { |
||
| 526 | var counter = 0; |
||
| 527 | |||
| 528 | return function(prefix) { |
||
| 529 | var guid = new Date().getTime().toString(32), i; |
||
| 530 | |||
| 531 | for (i = 0; i < 5; i++) { |
||
| 532 | guid += Math.floor(Math.random() * 65535).toString(32); |
||
| 533 | } |
||
| 534 | |||
| 535 | return (prefix || 'o_') + guid + (counter++).toString(32); |
||
| 536 | }; |
||
| 537 | }()); |
||
| 538 | |||
| 539 | |||
| 540 | /** |
||
| 541 | Trims white spaces around the string |
||
| 542 | |||
| 543 | @method trim |
||
| 544 | @static |
||
| 545 | @param {String} str |
||
| 546 | @return {String} |
||
| 547 | */ |
||
| 548 | function trim(str) { |
||
| 549 | if (!str) { |
||
| 550 | return str; |
||
| 551 | } |
||
| 552 | return String.prototype.trim ? String.prototype.trim.call(str) : str.toString().replace(/^\s*/, '').replace(/\s*$/, ''); |
||
| 553 | } |
||
| 554 | |||
| 555 | |||
| 556 | /** |
||
| 557 | Parses the specified size string into a byte value. For example 10kb becomes 10240. |
||
| 558 | |||
| 559 | @method parseSizeStr |
||
| 560 | @static |
||
| 561 | @param {String/Number} size String to parse or number to just pass through. |
||
| 562 | @return {Number} Size in bytes. |
||
| 563 | */ |
||
| 564 | function parseSizeStr(size) { |
||
| 565 | if (typeof(size) !== 'string') { |
||
| 566 | return size; |
||
| 567 | } |
||
| 568 | |||
| 569 | var muls = { |
||
| 570 | t: 1099511627776, |
||
| 571 | g: 1073741824, |
||
| 572 | m: 1048576, |
||
| 573 | k: 1024 |
||
| 574 | }, |
||
| 575 | mul; |
||
| 576 | |||
| 577 | size = /^([0-9\.]+)([tmgk]?)$/.exec(size.toLowerCase().replace(/[^0-9\.tmkg]/g, '')); |
||
| 578 | mul = size[2]; |
||
| 579 | size = +size[1]; |
||
| 580 | |||
| 581 | if (muls.hasOwnProperty(mul)) { |
||
| 582 | size *= muls[mul]; |
||
| 583 | } |
||
| 584 | return Math.floor(size); |
||
| 585 | } |
||
| 586 | |||
| 587 | |||
| 588 | /** |
||
| 589 | * Pseudo sprintf implementation - simple way to replace tokens with specified values. |
||
| 590 | * |
||
| 591 | * @param {String} str String with tokens |
||
| 592 | * @return {String} String with replaced tokens |
||
| 593 | */ |
||
| 594 | function sprintf(str) { |
||
| 595 | var args = [].slice.call(arguments, 1); |
||
| 596 | |||
| 597 | return str.replace(/%([a-z])/g, function($0, $1) { |
||
| 598 | var value = args.shift(); |
||
| 599 | |||
| 600 | switch ($1) { |
||
| 601 | case 's': |
||
| 602 | return value + ''; |
||
| 603 | |||
| 604 | case 'd': |
||
| 605 | return parseInt(value, 10); |
||
| 606 | |||
| 607 | case 'f': |
||
| 608 | return parseFloat(value); |
||
| 609 | |||
| 610 | case 'c': |
||
| 611 | return ''; |
||
| 612 | |||
| 613 | default: |
||
| 614 | return value; |
||
| 615 | } |
||
| 616 | }); |
||
| 617 | } |
||
| 618 | |||
| 619 | |||
| 620 | |||
| 621 | function delay(cb, timeout) { |
||
| 622 | var self = this; |
||
| 623 | setTimeout(function() { |
||
| 624 | cb.call(self); |
||
| 625 | }, timeout || 1); |
||
| 626 | } |
||
| 627 | |||
| 628 | |||
| 629 | return { |
||
| 630 | guid: guid, |
||
| 631 | typeOf: typeOf, |
||
| 632 | extend: extend, |
||
| 633 | extendIf: extendIf, |
||
| 634 | extendImmutable: extendImmutable, |
||
| 635 | extendImmutableIf: extendImmutableIf, |
||
| 636 | clone: clone, |
||
| 637 | inherit: inherit, |
||
| 638 | each: each, |
||
| 639 | isEmptyObj: isEmptyObj, |
||
| 640 | inSeries: inSeries, |
||
| 641 | inParallel: inParallel, |
||
| 642 | inArray: inArray, |
||
| 643 | arrayDiff: arrayDiff, |
||
| 644 | arrayIntersect: arrayIntersect, |
||
| 645 | toArray: toArray, |
||
| 646 | trim: trim, |
||
| 647 | sprintf: sprintf, |
||
| 648 | parseSizeStr: parseSizeStr, |
||
| 649 | delay: delay |
||
| 650 | }; |
||
| 651 | }); |
||
| 652 | |||
| 653 | // Included from: src/javascript/core/utils/Encode.js |
||
| 654 | |||
| 655 | /** |
||
| 656 | * Encode.js |
||
| 657 | * |
||
| 658 | * Copyright 2013, Moxiecode Systems AB |
||
| 659 | * Released under GPL License. |
||
| 660 | * |
||
| 661 | * License: http://www.plupload.com/license |
||
| 662 | * Contributing: http://www.plupload.com/contributing |
||
| 663 | */ |
||
| 664 | |||
| 665 | /** |
||
| 666 | @class moxie/core/utils/Encode |
||
| 667 | @public |
||
| 668 | @static |
||
| 669 | */ |
||
| 670 | |||
| 671 | define('moxie/core/utils/Encode', [], function() { |
||
| 672 | |||
| 673 | /** |
||
| 674 | Encode string with UTF-8 |
||
| 675 | |||
| 676 | @method utf8_encode |
||
| 677 | @static |
||
| 678 | @param {String} str String to encode |
||
| 679 | @return {String} UTF-8 encoded string |
||
| 680 | */ |
||
| 681 | var utf8_encode = function(str) { |
||
| 682 | return unescape(encodeURIComponent(str)); |
||
| 683 | }; |
||
| 684 | |||
| 685 | /** |
||
| 686 | Decode UTF-8 encoded string |
||
| 687 | |||
| 688 | @method utf8_decode |
||
| 689 | @static |
||
| 690 | @param {String} str String to decode |
||
| 691 | @return {String} Decoded string |
||
| 692 | */ |
||
| 693 | var utf8_decode = function(str_data) { |
||
| 694 | return decodeURIComponent(escape(str_data)); |
||
| 695 | }; |
||
| 696 | |||
| 697 | /** |
||
| 698 | Decode Base64 encoded string (uses browser's default method if available), |
||
| 699 | from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_decode.js |
||
| 700 | |||
| 701 | @method atob |
||
| 702 | @static |
||
| 703 | @param {String} data String to decode |
||
| 704 | @return {String} Decoded string |
||
| 705 | */ |
||
| 706 | var atob = function(data, utf8) { |
||
| 707 | if (typeof(window.atob) === 'function') { |
||
| 708 | return utf8 ? utf8_decode(window.atob(data)) : window.atob(data); |
||
| 709 | } |
||
| 710 | |||
| 711 | // http://kevin.vanzonneveld.net |
||
| 712 | // + original by: Tyler Akins (http://rumkin.com) |
||
| 713 | // + improved by: Thunder.m |
||
| 714 | // + input by: Aman Gupta |
||
| 715 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
||
| 716 | // + bugfixed by: Onno Marsman |
||
| 717 | // + bugfixed by: Pellentesque Malesuada |
||
| 718 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
||
| 719 | // + input by: Brett Zamir (http://brett-zamir.me) |
||
| 720 | // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
||
| 721 | // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); |
||
| 722 | // * returns 1: 'Kevin van Zonneveld' |
||
| 723 | // mozilla has this native |
||
| 724 | // - but breaks in 2.0.0.12! |
||
| 725 | //if (typeof this.window.atob == 'function') { |
||
| 726 | // return atob(data); |
||
| 727 | //} |
||
| 728 | var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; |
||
| 729 | var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, |
||
| 730 | ac = 0, |
||
| 731 | dec = "", |
||
| 732 | tmp_arr = []; |
||
| 733 | |||
| 734 | if (!data) { |
||
| 735 | return data; |
||
| 736 | } |
||
| 737 | |||
| 738 | data += ''; |
||
| 739 | |||
| 740 | do { // unpack four hexets into three octets using index points in b64 |
||
| 741 | h1 = b64.indexOf(data.charAt(i++)); |
||
| 742 | h2 = b64.indexOf(data.charAt(i++)); |
||
| 743 | h3 = b64.indexOf(data.charAt(i++)); |
||
| 744 | h4 = b64.indexOf(data.charAt(i++)); |
||
| 745 | |||
| 746 | bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; |
||
| 747 | |||
| 748 | o1 = bits >> 16 & 0xff; |
||
| 749 | o2 = bits >> 8 & 0xff; |
||
| 750 | o3 = bits & 0xff; |
||
| 751 | |||
| 752 | if (h3 == 64) { |
||
| 753 | tmp_arr[ac++] = String.fromCharCode(o1); |
||
| 754 | } else if (h4 == 64) { |
||
| 755 | tmp_arr[ac++] = String.fromCharCode(o1, o2); |
||
| 756 | } else { |
||
| 757 | tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); |
||
| 758 | } |
||
| 759 | } while (i < data.length); |
||
| 760 | |||
| 761 | dec = tmp_arr.join(''); |
||
| 762 | |||
| 763 | return utf8 ? utf8_decode(dec) : dec; |
||
| 764 | }; |
||
| 765 | |||
| 766 | /** |
||
| 767 | Base64 encode string (uses browser's default method if available), |
||
| 768 | from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_encode.js |
||
| 769 | |||
| 770 | @method btoa |
||
| 771 | @static |
||
| 772 | @param {String} data String to encode |
||
| 773 | @return {String} Base64 encoded string |
||
| 774 | */ |
||
| 775 | var btoa = function(data, utf8) { |
||
| 776 | if (utf8) { |
||
| 777 | data = utf8_encode(data); |
||
| 778 | } |
||
| 779 | |||
| 780 | if (typeof(window.btoa) === 'function') { |
||
| 781 | return window.btoa(data); |
||
| 782 | } |
||
| 783 | |||
| 784 | // http://kevin.vanzonneveld.net |
||
| 785 | // + original by: Tyler Akins (http://rumkin.com) |
||
| 786 | // + improved by: Bayron Guevara |
||
| 787 | // + improved by: Thunder.m |
||
| 788 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
||
| 789 | // + bugfixed by: Pellentesque Malesuada |
||
| 790 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
||
| 791 | // + improved by: Rafał Kukawski (http://kukawski.pl) |
||
| 792 | // * example 1: base64_encode('Kevin van Zonneveld'); |
||
| 793 | // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' |
||
| 794 | // mozilla has this native |
||
| 795 | // - but breaks in 2.0.0.12! |
||
| 796 | var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; |
||
| 797 | var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, |
||
| 798 | ac = 0, |
||
| 799 | enc = "", |
||
| 800 | tmp_arr = []; |
||
| 801 | |||
| 802 | if (!data) { |
||
| 803 | return data; |
||
| 804 | } |
||
| 805 | |||
| 806 | do { // pack three octets into four hexets |
||
| 807 | o1 = data.charCodeAt(i++); |
||
| 808 | o2 = data.charCodeAt(i++); |
||
| 809 | o3 = data.charCodeAt(i++); |
||
| 810 | |||
| 811 | bits = o1 << 16 | o2 << 8 | o3; |
||
| 812 | |||
| 813 | h1 = bits >> 18 & 0x3f; |
||
| 814 | h2 = bits >> 12 & 0x3f; |
||
| 815 | h3 = bits >> 6 & 0x3f; |
||
| 816 | h4 = bits & 0x3f; |
||
| 817 | |||
| 818 | // use hexets to index into b64, and append result to encoded string |
||
| 819 | tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); |
||
| 820 | } while (i < data.length); |
||
| 821 | |||
| 822 | enc = tmp_arr.join(''); |
||
| 823 | |||
| 824 | var r = data.length % 3; |
||
| 825 | |||
| 826 | return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); |
||
| 827 | }; |
||
| 828 | |||
| 829 | |||
| 830 | return { |
||
| 831 | utf8_encode: utf8_encode, |
||
| 832 | utf8_decode: utf8_decode, |
||
| 833 | atob: atob, |
||
| 834 | btoa: btoa |
||
| 835 | }; |
||
| 836 | }); |
||
| 837 | |||
| 838 | // Included from: src/javascript/core/utils/Env.js |
||
| 839 | |||
| 840 | /** |
||
| 841 | * Env.js |
||
| 842 | * |
||
| 843 | * Copyright 2013, Moxiecode Systems AB |
||
| 844 | * Released under GPL License. |
||
| 845 | * |
||
| 846 | * License: http://www.plupload.com/license |
||
| 847 | * Contributing: http://www.plupload.com/contributing |
||
| 848 | */ |
||
| 849 | |||
| 850 | /** |
||
| 851 | @class moxie/core/utils/Env |
||
| 852 | @public |
||
| 853 | @static |
||
| 854 | */ |
||
| 855 | |||
| 856 | define("moxie/core/utils/Env", [ |
||
| 857 | "moxie/core/utils/Basic" |
||
| 858 | ], function(Basic) { |
||
| 859 | |||
| 860 | /** |
||
| 861 | * UAParser.js v0.7.7 |
||
| 862 | * Lightweight JavaScript-based User-Agent string parser |
||
| 863 | * https://github.com/faisalman/ua-parser-js |
||
| 864 | * |
||
| 865 | * Copyright © 2012-2015 Faisal Salman <[email protected]> |
||
| 866 | * Dual licensed under GPLv2 & MIT |
||
| 867 | */ |
||
| 868 | var UAParser = (function (undefined) { |
||
| 869 | |||
| 870 | ////////////// |
||
| 871 | // Constants |
||
| 872 | ///////////// |
||
| 873 | |||
| 874 | |||
| 875 | var EMPTY = '', |
||
| 876 | UNKNOWN = '?', |
||
| 877 | FUNC_TYPE = 'function', |
||
| 878 | UNDEF_TYPE = 'undefined', |
||
| 879 | OBJ_TYPE = 'object', |
||
| 880 | MAJOR = 'major', |
||
| 881 | MODEL = 'model', |
||
| 882 | NAME = 'name', |
||
| 883 | TYPE = 'type', |
||
| 884 | VENDOR = 'vendor', |
||
| 885 | VERSION = 'version', |
||
| 886 | ARCHITECTURE= 'architecture', |
||
| 887 | CONSOLE = 'console', |
||
| 888 | MOBILE = 'mobile', |
||
| 889 | TABLET = 'tablet'; |
||
| 890 | |||
| 891 | |||
| 892 | /////////// |
||
| 893 | // Helper |
||
| 894 | ////////// |
||
| 895 | |||
| 896 | |||
| 897 | var util = { |
||
| 898 | has : function (str1, str2) { |
||
| 899 | return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1; |
||
| 900 | }, |
||
| 901 | lowerize : function (str) { |
||
| 902 | return str.toLowerCase(); |
||
| 903 | } |
||
| 904 | }; |
||
| 905 | |||
| 906 | |||
| 907 | /////////////// |
||
| 908 | // Map helper |
||
| 909 | ////////////// |
||
| 910 | |||
| 911 | |||
| 912 | var mapper = { |
||
| 913 | |||
| 914 | rgx : function () { |
||
| 915 | |||
| 916 | // loop through all regexes maps |
||
| 917 | for (var result, i = 0, j, k, p, q, matches, match, args = arguments; i < args.length; i += 2) { |
||
| 918 | |||
| 919 | var regex = args[i], // even sequence (0,2,4,..) |
||
| 920 | props = args[i + 1]; // odd sequence (1,3,5,..) |
||
| 921 | |||
| 922 | // construct object barebones |
||
| 923 | if (typeof(result) === UNDEF_TYPE) { |
||
| 924 | result = {}; |
||
| 925 | for (p in props) { |
||
| 926 | q = props[p]; |
||
| 927 | if (typeof(q) === OBJ_TYPE) { |
||
| 928 | result[q[0]] = undefined; |
||
| 929 | } else { |
||
| 930 | result[q] = undefined; |
||
| 931 | } |
||
| 932 | } |
||
| 933 | } |
||
| 934 | |||
| 935 | // try matching uastring with regexes |
||
| 936 | for (j = k = 0; j < regex.length; j++) { |
||
| 937 | matches = regex[j].exec(this.getUA()); |
||
| 938 | if (!!matches) { |
||
| 939 | for (p = 0; p < props.length; p++) { |
||
| 940 | match = matches[++k]; |
||
| 941 | q = props[p]; |
||
| 942 | // check if given property is actually array |
||
| 943 | if (typeof(q) === OBJ_TYPE && q.length > 0) { |
||
| 944 | if (q.length == 2) { |
||
| 945 | if (typeof(q[1]) == FUNC_TYPE) { |
||
| 946 | // assign modified match |
||
| 947 | result[q[0]] = q[1].call(this, match); |
||
| 948 | } else { |
||
| 949 | // assign given value, ignore regex match |
||
| 950 | result[q[0]] = q[1]; |
||
| 951 | } |
||
| 952 | } else if (q.length == 3) { |
||
| 953 | // check whether function or regex |
||
| 954 | if (typeof(q[1]) === FUNC_TYPE && !(q[1].exec && q[1].test)) { |
||
| 955 | // call function (usually string mapper) |
||
| 956 | result[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; |
||
| 957 | } else { |
||
| 958 | // sanitize match using given regex |
||
| 959 | result[q[0]] = match ? match.replace(q[1], q[2]) : undefined; |
||
| 960 | } |
||
| 961 | } else if (q.length == 4) { |
||
| 962 | result[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; |
||
| 963 | } |
||
| 964 | } else { |
||
| 965 | result[q] = match ? match : undefined; |
||
| 966 | } |
||
| 967 | } |
||
| 968 | break; |
||
| 969 | } |
||
| 970 | } |
||
| 971 | |||
| 972 | if(!!matches) break; // break the loop immediately if match found |
||
| 973 | } |
||
| 974 | return result; |
||
| 975 | }, |
||
| 976 | |||
| 977 | str : function (str, map) { |
||
| 978 | |||
| 979 | for (var i in map) { |
||
| 980 | // check if array |
||
| 981 | if (typeof(map[i]) === OBJ_TYPE && map[i].length > 0) { |
||
| 982 | for (var j = 0; j < map[i].length; j++) { |
||
| 983 | if (util.has(map[i][j], str)) { |
||
| 984 | return (i === UNKNOWN) ? undefined : i; |
||
| 985 | } |
||
| 986 | } |
||
| 987 | } else if (util.has(map[i], str)) { |
||
| 988 | return (i === UNKNOWN) ? undefined : i; |
||
| 989 | } |
||
| 990 | } |
||
| 991 | return str; |
||
| 992 | } |
||
| 993 | }; |
||
| 994 | |||
| 995 | |||
| 996 | /////////////// |
||
| 997 | // String map |
||
| 998 | ////////////// |
||
| 999 | |||
| 1000 | |||
| 1001 | var maps = { |
||
| 1002 | |||
| 1003 | browser : { |
||
| 1004 | oldsafari : { |
||
| 1005 | major : { |
||
| 1006 | '1' : ['/8', '/1', '/3'], |
||
| 1007 | '2' : '/4', |
||
| 1008 | '?' : '/' |
||
| 1009 | }, |
||
| 1010 | version : { |
||
| 1011 | '1.0' : '/8', |
||
| 1012 | '1.2' : '/1', |
||
| 1013 | '1.3' : '/3', |
||
| 1014 | '2.0' : '/412', |
||
| 1015 | '2.0.2' : '/416', |
||
| 1016 | '2.0.3' : '/417', |
||
| 1017 | '2.0.4' : '/419', |
||
| 1018 | '?' : '/' |
||
| 1019 | } |
||
| 1020 | } |
||
| 1021 | }, |
||
| 1022 | |||
| 1023 | device : { |
||
| 1024 | sprint : { |
||
| 1025 | model : { |
||
| 1026 | 'Evo Shift 4G' : '7373KT' |
||
| 1027 | }, |
||
| 1028 | vendor : { |
||
| 1029 | 'HTC' : 'APA', |
||
| 1030 | 'Sprint' : 'Sprint' |
||
| 1031 | } |
||
| 1032 | } |
||
| 1033 | }, |
||
| 1034 | |||
| 1035 | os : { |
||
| 1036 | windows : { |
||
| 1037 | version : { |
||
| 1038 | 'ME' : '4.90', |
||
| 1039 | 'NT 3.11' : 'NT3.51', |
||
| 1040 | 'NT 4.0' : 'NT4.0', |
||
| 1041 | '2000' : 'NT 5.0', |
||
| 1042 | 'XP' : ['NT 5.1', 'NT 5.2'], |
||
| 1043 | 'Vista' : 'NT 6.0', |
||
| 1044 | '7' : 'NT 6.1', |
||
| 1045 | '8' : 'NT 6.2', |
||
| 1046 | '8.1' : 'NT 6.3', |
||
| 1047 | 'RT' : 'ARM' |
||
| 1048 | } |
||
| 1049 | } |
||
| 1050 | } |
||
| 1051 | }; |
||
| 1052 | |||
| 1053 | |||
| 1054 | ////////////// |
||
| 1055 | // Regex map |
||
| 1056 | ///////////// |
||
| 1057 | |||
| 1058 | |||
| 1059 | var regexes = { |
||
| 1060 | |||
| 1061 | browser : [[ |
||
| 1062 | |||
| 1063 | // Presto based |
||
| 1064 | /(opera\smini)\/([\w\.-]+)/i, // Opera Mini |
||
| 1065 | /(opera\s[mobiletab]+).+version\/([\w\.-]+)/i, // Opera Mobi/Tablet |
||
| 1066 | /(opera).+version\/([\w\.]+)/i, // Opera > 9.80 |
||
| 1067 | /(opera)[\/\s]+([\w\.]+)/i // Opera < 9.80 |
||
| 1068 | |||
| 1069 | ], [NAME, VERSION], [ |
||
| 1070 | |||
| 1071 | /\s(opr)\/([\w\.]+)/i // Opera Webkit |
||
| 1072 | ], [[NAME, 'Opera'], VERSION], [ |
||
| 1073 | |||
| 1074 | // Mixed |
||
| 1075 | /(kindle)\/([\w\.]+)/i, // Kindle |
||
| 1076 | /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]+)*/i, |
||
| 1077 | // Lunascape/Maxthon/Netfront/Jasmine/Blazer |
||
| 1078 | |||
| 1079 | // Trident based |
||
| 1080 | /(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?([\w\.]*)/i, |
||
| 1081 | // Avant/IEMobile/SlimBrowser/Baidu |
||
| 1082 | /(?:ms|\()(ie)\s([\w\.]+)/i, // Internet Explorer |
||
| 1083 | |||
| 1084 | // Webkit/KHTML based |
||
| 1085 | /(rekonq)\/([\w\.]+)*/i, // Rekonq |
||
| 1086 | /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi)\/([\w\.-]+)/i |
||
| 1087 | // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron |
||
| 1088 | ], [NAME, VERSION], [ |
||
| 1089 | |||
| 1090 | /(trident).+rv[:\s]([\w\.]+).+like\sgecko/i // IE11 |
||
| 1091 | ], [[NAME, 'IE'], VERSION], [ |
||
| 1092 | |||
| 1093 | /(edge)\/((\d+)?[\w\.]+)/i // Microsoft Edge |
||
| 1094 | ], [NAME, VERSION], [ |
||
| 1095 | |||
| 1096 | /(yabrowser)\/([\w\.]+)/i // Yandex |
||
| 1097 | ], [[NAME, 'Yandex'], VERSION], [ |
||
| 1098 | |||
| 1099 | /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon |
||
| 1100 | ], [[NAME, /_/g, ' '], VERSION], [ |
||
| 1101 | |||
| 1102 | /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i, |
||
| 1103 | // Chrome/OmniWeb/Arora/Tizen/Nokia |
||
| 1104 | /(uc\s?browser|qqbrowser)[\/\s]?([\w\.]+)/i |
||
| 1105 | // UCBrowser/QQBrowser |
||
| 1106 | ], [NAME, VERSION], [ |
||
| 1107 | |||
| 1108 | /(dolfin)\/([\w\.]+)/i // Dolphin |
||
| 1109 | ], [[NAME, 'Dolphin'], VERSION], [ |
||
| 1110 | |||
| 1111 | /((?:android.+)crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS |
||
| 1112 | ], [[NAME, 'Chrome'], VERSION], [ |
||
| 1113 | |||
| 1114 | /XiaoMi\/MiuiBrowser\/([\w\.]+)/i // MIUI Browser |
||
| 1115 | ], [VERSION, [NAME, 'MIUI Browser']], [ |
||
| 1116 | |||
| 1117 | /android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)/i // Android Browser |
||
| 1118 | ], [VERSION, [NAME, 'Android Browser']], [ |
||
| 1119 | |||
| 1120 | /FBAV\/([\w\.]+);/i // Facebook App for iOS |
||
| 1121 | ], [VERSION, [NAME, 'Facebook']], [ |
||
| 1122 | |||
| 1123 | /version\/([\w\.]+).+?mobile\/\w+\s(safari)/i // Mobile Safari |
||
| 1124 | ], [VERSION, [NAME, 'Mobile Safari']], [ |
||
| 1125 | |||
| 1126 | /version\/([\w\.]+).+?(mobile\s?safari|safari)/i // Safari & Safari Mobile |
||
| 1127 | ], [VERSION, NAME], [ |
||
| 1128 | |||
| 1129 | /webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 |
||
| 1130 | ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [ |
||
| 1131 | |||
| 1132 | /(konqueror)\/([\w\.]+)/i, // Konqueror |
||
| 1133 | /(webkit|khtml)\/([\w\.]+)/i |
||
| 1134 | ], [NAME, VERSION], [ |
||
| 1135 | |||
| 1136 | // Gecko based |
||
| 1137 | /(navigator|netscape)\/([\w\.-]+)/i // Netscape |
||
| 1138 | ], [[NAME, 'Netscape'], VERSION], [ |
||
| 1139 | /(swiftfox)/i, // Swiftfox |
||
| 1140 | /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i, |
||
| 1141 | // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror |
||
| 1142 | /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/([\w\.-]+)/i, |
||
| 1143 | // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix |
||
| 1144 | /(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i, // Mozilla |
||
| 1145 | |||
| 1146 | // Other |
||
| 1147 | /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf)[\/\s]?([\w\.]+)/i, |
||
| 1148 | // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf |
||
| 1149 | /(links)\s\(([\w\.]+)/i, // Links |
||
| 1150 | /(gobrowser)\/?([\w\.]+)*/i, // GoBrowser |
||
| 1151 | /(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser |
||
| 1152 | /(mosaic)[\/\s]([\w\.]+)/i // Mosaic |
||
| 1153 | ], [NAME, VERSION] |
||
| 1154 | ], |
||
| 1155 | |||
| 1156 | engine : [[ |
||
| 1157 | |||
| 1158 | /windows.+\sedge\/([\w\.]+)/i // EdgeHTML |
||
| 1159 | ], [VERSION, [NAME, 'EdgeHTML']], [ |
||
| 1160 | |||
| 1161 | /(presto)\/([\w\.]+)/i, // Presto |
||
| 1162 | /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m |
||
| 1163 | /(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i, // KHTML/Tasman/Links |
||
| 1164 | /(icab)[\/\s]([23]\.[\d\.]+)/i // iCab |
||
| 1165 | ], [NAME, VERSION], [ |
||
| 1166 | |||
| 1167 | /rv\:([\w\.]+).*(gecko)/i // Gecko |
||
| 1168 | ], [VERSION, NAME] |
||
| 1169 | ], |
||
| 1170 | |||
| 1171 | os : [[ |
||
| 1172 | |||
| 1173 | // Windows based |
||
| 1174 | /microsoft\s(windows)\s(vista|xp)/i // Windows (iTunes) |
||
| 1175 | ], [NAME, VERSION], [ |
||
| 1176 | /(windows)\snt\s6\.2;\s(arm)/i, // Windows RT |
||
| 1177 | /(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i |
||
| 1178 | ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [ |
||
| 1179 | /(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i |
||
| 1180 | ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [ |
||
| 1181 | |||
| 1182 | // Mobile/Embedded OS |
||
| 1183 | /\((bb)(10);/i // BlackBerry 10 |
||
| 1184 | ], [[NAME, 'BlackBerry'], VERSION], [ |
||
| 1185 | /(blackberry)\w*\/?([\w\.]+)*/i, // Blackberry |
||
| 1186 | /(tizen)[\/\s]([\w\.]+)/i, // Tizen |
||
| 1187 | /(android|webos|palm\os|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]+)*/i, |
||
| 1188 | // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki |
||
| 1189 | /linux;.+(sailfish);/i // Sailfish OS |
||
| 1190 | ], [NAME, VERSION], [ |
||
| 1191 | /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i // Symbian |
||
| 1192 | ], [[NAME, 'Symbian'], VERSION], [ |
||
| 1193 | /\((series40);/i // Series 40 |
||
| 1194 | ], [NAME], [ |
||
| 1195 | /mozilla.+\(mobile;.+gecko.+firefox/i // Firefox OS |
||
| 1196 | ], [[NAME, 'Firefox OS'], VERSION], [ |
||
| 1197 | |||
| 1198 | // Console |
||
| 1199 | /(nintendo|playstation)\s([wids3portablevu]+)/i, // Nintendo/Playstation |
||
| 1200 | |||
| 1201 | // GNU/Linux based |
||
| 1202 | /(mint)[\/\s\(]?(\w+)*/i, // Mint |
||
| 1203 | /(mageia|vectorlinux)[;\s]/i, // Mageia/VectorLinux |
||
| 1204 | /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?([\w\.-]+)*/i, |
||
| 1205 | // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware |
||
| 1206 | // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus |
||
| 1207 | /(hurd|linux)\s?([\w\.]+)*/i, // Hurd/Linux |
||
| 1208 | /(gnu)\s?([\w\.]+)*/i // GNU |
||
| 1209 | ], [NAME, VERSION], [ |
||
| 1210 | |||
| 1211 | /(cros)\s[\w]+\s([\w\.]+\w)/i // Chromium OS |
||
| 1212 | ], [[NAME, 'Chromium OS'], VERSION],[ |
||
| 1213 | |||
| 1214 | // Solaris |
||
| 1215 | /(sunos)\s?([\w\.]+\d)*/i // Solaris |
||
| 1216 | ], [[NAME, 'Solaris'], VERSION], [ |
||
| 1217 | |||
| 1218 | // BSD based |
||
| 1219 | /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly |
||
| 1220 | ], [NAME, VERSION],[ |
||
| 1221 | |||
| 1222 | /(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i // iOS |
||
| 1223 | ], [[NAME, 'iOS'], [VERSION, /_/g, '.']], [ |
||
| 1224 | |||
| 1225 | /(mac\sos\sx)\s?([\w\s\.]+\w)*/i, |
||
| 1226 | /(macintosh|mac(?=_powerpc)\s)/i // Mac OS |
||
| 1227 | ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [ |
||
| 1228 | |||
| 1229 | // Other |
||
| 1230 | /((?:open)?solaris)[\/\s-]?([\w\.]+)*/i, // Solaris |
||
| 1231 | /(haiku)\s(\w+)/i, // Haiku |
||
| 1232 | /(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i, // AIX |
||
| 1233 | /(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms)/i, |
||
| 1234 | // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS |
||
| 1235 | /(unix)\s?([\w\.]+)*/i // UNIX |
||
| 1236 | ], [NAME, VERSION] |
||
| 1237 | ] |
||
| 1238 | }; |
||
| 1239 | |||
| 1240 | |||
| 1241 | ///////////////// |
||
| 1242 | // Constructor |
||
| 1243 | //////////////// |
||
| 1244 | |||
| 1245 | |||
| 1246 | var UAParser = function (uastring) { |
||
| 1247 | |||
| 1248 | var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY); |
||
| 1249 | |||
| 1250 | this.getBrowser = function () { |
||
| 1251 | return mapper.rgx.apply(this, regexes.browser); |
||
| 1252 | }; |
||
| 1253 | this.getEngine = function () { |
||
| 1254 | return mapper.rgx.apply(this, regexes.engine); |
||
| 1255 | }; |
||
| 1256 | this.getOS = function () { |
||
| 1257 | return mapper.rgx.apply(this, regexes.os); |
||
| 1258 | }; |
||
| 1259 | this.getResult = function() { |
||
| 1260 | return { |
||
| 1261 | ua : this.getUA(), |
||
| 1262 | browser : this.getBrowser(), |
||
| 1263 | engine : this.getEngine(), |
||
| 1264 | os : this.getOS() |
||
| 1265 | }; |
||
| 1266 | }; |
||
| 1267 | this.getUA = function () { |
||
| 1268 | return ua; |
||
| 1269 | }; |
||
| 1270 | this.setUA = function (uastring) { |
||
| 1271 | ua = uastring; |
||
| 1272 | return this; |
||
| 1273 | }; |
||
| 1274 | this.setUA(ua); |
||
| 1275 | }; |
||
| 1276 | |||
| 1277 | return UAParser; |
||
| 1278 | })(); |
||
| 1279 | |||
| 1280 | |||
| 1281 | function version_compare(v1, v2, operator) { |
||
| 1282 | // From: http://phpjs.org/functions |
||
| 1283 | // + original by: Philippe Jausions (http://pear.php.net/user/jausions) |
||
| 1284 | // + original by: Aidan Lister (http://aidanlister.com/) |
||
| 1285 | // + reimplemented by: Kankrelune (http://www.webfaktory.info/) |
||
| 1286 | // + improved by: Brett Zamir (http://brett-zamir.me) |
||
| 1287 | // + improved by: Scott Baker |
||
| 1288 | // + improved by: Theriault |
||
| 1289 | // * example 1: version_compare('8.2.5rc', '8.2.5a'); |
||
| 1290 | // * returns 1: 1 |
||
| 1291 | // * example 2: version_compare('8.2.50', '8.2.52', '<'); |
||
| 1292 | // * returns 2: true |
||
| 1293 | // * example 3: version_compare('5.3.0-dev', '5.3.0'); |
||
| 1294 | // * returns 3: -1 |
||
| 1295 | // * example 4: version_compare('4.1.0.52','4.01.0.51'); |
||
| 1296 | // * returns 4: 1 |
||
| 1297 | |||
| 1298 | // Important: compare must be initialized at 0. |
||
| 1299 | var i = 0, |
||
| 1300 | x = 0, |
||
| 1301 | compare = 0, |
||
| 1302 | // vm maps textual PHP versions to negatives so they're less than 0. |
||
| 1303 | // PHP currently defines these as CASE-SENSITIVE. It is important to |
||
| 1304 | // leave these as negatives so that they can come before numerical versions |
||
| 1305 | // and as if no letters were there to begin with. |
||
| 1306 | // (1alpha is < 1 and < 1.1 but > 1dev1) |
||
| 1307 | // If a non-numerical value can't be mapped to this table, it receives |
||
| 1308 | // -7 as its value. |
||
| 1309 | vm = { |
||
| 1310 | 'dev': -6, |
||
| 1311 | 'alpha': -5, |
||
| 1312 | 'a': -5, |
||
| 1313 | 'beta': -4, |
||
| 1314 | 'b': -4, |
||
| 1315 | 'RC': -3, |
||
| 1316 | 'rc': -3, |
||
| 1317 | '#': -2, |
||
| 1318 | 'p': 1, |
||
| 1319 | 'pl': 1 |
||
| 1320 | }, |
||
| 1321 | // This function will be called to prepare each version argument. |
||
| 1322 | // It replaces every _, -, and + with a dot. |
||
| 1323 | // It surrounds any nonsequence of numbers/dots with dots. |
||
| 1324 | // It replaces sequences of dots with a single dot. |
||
| 1325 | // version_compare('4..0', '4.0') == 0 |
||
| 1326 | // Important: A string of 0 length needs to be converted into a value |
||
| 1327 | // even less than an unexisting value in vm (-7), hence [-8]. |
||
| 1328 | // It's also important to not strip spaces because of this. |
||
| 1329 | // version_compare('', ' ') == 1 |
||
| 1330 | prepVersion = function (v) { |
||
| 1331 | v = ('' + v).replace(/[_\-+]/g, '.'); |
||
| 1332 | v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.'); |
||
| 1333 | return (!v.length ? [-8] : v.split('.')); |
||
| 1334 | }, |
||
| 1335 | // This converts a version component to a number. |
||
| 1336 | // Empty component becomes 0. |
||
| 1337 | // Non-numerical component becomes a negative number. |
||
| 1338 | // Numerical component becomes itself as an integer. |
||
| 1339 | numVersion = function (v) { |
||
| 1340 | return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10)); |
||
| 1341 | }; |
||
| 1342 | |||
| 1343 | v1 = prepVersion(v1); |
||
| 1344 | v2 = prepVersion(v2); |
||
| 1345 | x = Math.max(v1.length, v2.length); |
||
| 1346 | for (i = 0; i < x; i++) { |
||
| 1347 | if (v1[i] == v2[i]) { |
||
| 1348 | continue; |
||
| 1349 | } |
||
| 1350 | v1[i] = numVersion(v1[i]); |
||
| 1351 | v2[i] = numVersion(v2[i]); |
||
| 1352 | if (v1[i] < v2[i]) { |
||
| 1353 | compare = -1; |
||
| 1354 | break; |
||
| 1355 | } else if (v1[i] > v2[i]) { |
||
| 1356 | compare = 1; |
||
| 1357 | break; |
||
| 1358 | } |
||
| 1359 | } |
||
| 1360 | if (!operator) { |
||
| 1361 | return compare; |
||
| 1362 | } |
||
| 1363 | |||
| 1364 | // Important: operator is CASE-SENSITIVE. |
||
| 1365 | // "No operator" seems to be treated as "<." |
||
| 1366 | // Any other values seem to make the function return null. |
||
| 1367 | switch (operator) { |
||
| 1368 | case '>': |
||
| 1369 | case 'gt': |
||
| 1370 | return (compare > 0); |
||
| 1371 | case '>=': |
||
| 1372 | case 'ge': |
||
| 1373 | return (compare >= 0); |
||
| 1374 | case '<=': |
||
| 1375 | case 'le': |
||
| 1376 | return (compare <= 0); |
||
| 1377 | case '==': |
||
| 1378 | case '=': |
||
| 1379 | case 'eq': |
||
| 1380 | return (compare === 0); |
||
| 1381 | case '<>': |
||
| 1382 | case '!=': |
||
| 1383 | case 'ne': |
||
| 1384 | return (compare !== 0); |
||
| 1385 | case '': |
||
| 1386 | case '<': |
||
| 1387 | case 'lt': |
||
| 1388 | return (compare < 0); |
||
| 1389 | default: |
||
| 1390 | return null; |
||
| 1391 | } |
||
| 1392 | } |
||
| 1393 | |||
| 1394 | |||
| 1395 | var can = (function() { |
||
| 1396 | var caps = { |
||
| 1397 | access_global_ns: function () { |
||
| 1398 | return !!window.moxie; |
||
| 1399 | }, |
||
| 1400 | |||
| 1401 | define_property: (function() { |
||
| 1402 | /* // currently too much extra code required, not exactly worth it |
||
| 1403 | try { // as of IE8, getters/setters are supported only on DOM elements |
||
| 1404 | var obj = {}; |
||
| 1405 | if (Object.defineProperty) { |
||
| 1406 | Object.defineProperty(obj, 'prop', { |
||
| 1407 | enumerable: true, |
||
| 1408 | configurable: true |
||
| 1409 | }); |
||
| 1410 | return true; |
||
| 1411 | } |
||
| 1412 | } catch(ex) {} |
||
| 1413 | |||
| 1414 | if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { |
||
| 1415 | return true; |
||
| 1416 | }*/ |
||
| 1417 | return false; |
||
| 1418 | }()), |
||
| 1419 | |||
| 1420 | create_canvas: function() { |
||
| 1421 | // On the S60 and BB Storm, getContext exists, but always returns undefined |
||
| 1422 | // so we actually have to call getContext() to verify |
||
| 1423 | // github.com/Modernizr/Modernizr/issues/issue/97/ |
||
| 1424 | var el = document.createElement('canvas'); |
||
| 1425 | var isSupported = !!(el.getContext && el.getContext('2d')); |
||
| 1426 | caps.create_canvas = isSupported; |
||
| 1427 | return isSupported; |
||
| 1428 | }, |
||
| 1429 | |||
| 1430 | return_response_type: function(responseType) { |
||
| 1431 | try { |
||
| 1432 | if (Basic.inArray(responseType, ['', 'text', 'document']) !== -1) { |
||
| 1433 | return true; |
||
| 1434 | } else if (window.XMLHttpRequest) { |
||
| 1435 | var xhr = new XMLHttpRequest(); |
||
| 1436 | xhr.open('get', '/'); // otherwise Gecko throws an exception |
||
| 1437 | if ('responseType' in xhr) { |
||
| 1438 | xhr.responseType = responseType; |
||
| 1439 | // as of 23.0.1271.64, Chrome switched from throwing exception to merely logging it to the console (why? o why?) |
||
| 1440 | if (xhr.responseType !== responseType) { |
||
| 1441 | return false; |
||
| 1442 | } |
||
| 1443 | return true; |
||
| 1444 | } |
||
| 1445 | } |
||
| 1446 | } catch (ex) {} |
||
| 1447 | return false; |
||
| 1448 | }, |
||
| 1449 | |||
| 1450 | use_blob_uri: function() { |
||
| 1451 | var URL = window.URL; |
||
| 1452 | caps.use_blob_uri = (URL && |
||
| 1453 | 'createObjectURL' in URL && |
||
| 1454 | 'revokeObjectURL' in URL && |
||
| 1455 | (Env.browser !== 'IE' || Env.verComp(Env.version, '11.0.46', '>=')) // IE supports createObjectURL, but not fully, for example it fails to use it as a src for the image |
||
| 1456 | ); |
||
| 1457 | return caps.use_blob_uri; |
||
| 1458 | }, |
||
| 1459 | |||
| 1460 | // ideas for this heavily come from Modernizr (http://modernizr.com/) |
||
| 1461 | use_data_uri: (function() { |
||
| 1462 | var du = new Image(); |
||
| 1463 | |||
| 1464 | du.onload = function() { |
||
| 1465 | caps.use_data_uri = (du.width === 1 && du.height === 1); |
||
| 1466 | }; |
||
| 1467 | |||
| 1468 | setTimeout(function() { |
||
| 1469 | du.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="; |
||
| 1470 | }, 1); |
||
| 1471 | return false; |
||
| 1472 | }()), |
||
| 1473 | |||
| 1474 | use_data_uri_over32kb: function() { // IE8 |
||
| 1475 | return caps.use_data_uri && (Env.browser !== 'IE' || Env.version >= 9); |
||
| 1476 | }, |
||
| 1477 | |||
| 1478 | use_data_uri_of: function(bytes) { |
||
| 1479 | return (caps.use_data_uri && bytes < 33000 || caps.use_data_uri_over32kb()); |
||
| 1480 | }, |
||
| 1481 | |||
| 1482 | use_fileinput: function() { |
||
| 1483 | if (navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/)) { |
||
| 1484 | return false; |
||
| 1485 | } |
||
| 1486 | |||
| 1487 | var el = document.createElement('input'); |
||
| 1488 | el.setAttribute('type', 'file'); |
||
| 1489 | return caps.use_fileinput = !el.disabled; |
||
| 1490 | }, |
||
| 1491 | |||
| 1492 | use_webgl: function() { |
||
| 1493 | var canvas = document.createElement('canvas'); |
||
| 1494 | var gl = null, isSupported; |
||
| 1495 | try { |
||
| 1496 | gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); |
||
| 1497 | } |
||
| 1498 | catch(e) {} |
||
| 1499 | |||
| 1500 | if (!gl) { // it seems that sometimes it doesn't throw exception, but still fails to get context |
||
| 1501 | gl = null; |
||
| 1502 | } |
||
| 1503 | |||
| 1504 | isSupported = !!gl; |
||
| 1505 | caps.use_webgl = isSupported; // save result of our check |
||
| 1506 | canvas = undefined; |
||
| 1507 | return isSupported; |
||
| 1508 | } |
||
| 1509 | }; |
||
| 1510 | |||
| 1511 | return function(cap) { |
||
| 1512 | var args = [].slice.call(arguments); |
||
| 1513 | args.shift(); // shift of cap |
||
| 1514 | return Basic.typeOf(caps[cap]) === 'function' ? caps[cap].apply(this, args) : !!caps[cap]; |
||
| 1515 | }; |
||
| 1516 | }()); |
||
| 1517 | |||
| 1518 | |||
| 1519 | var uaResult = new UAParser().getResult(); |
||
| 1520 | |||
| 1521 | |||
| 1522 | var Env = { |
||
| 1523 | can: can, |
||
| 1524 | |||
| 1525 | uaParser: UAParser, |
||
| 1526 | |||
| 1527 | browser: uaResult.browser.name, |
||
| 1528 | version: uaResult.browser.version, |
||
| 1529 | os: uaResult.os.name, // everybody intuitively types it in a lowercase for some reason |
||
| 1530 | osVersion: uaResult.os.version, |
||
| 1531 | |||
| 1532 | verComp: version_compare, |
||
| 1533 | |||
| 1534 | swf_url: "../flash/Moxie.swf", |
||
| 1535 | xap_url: "../silverlight/Moxie.xap", |
||
| 1536 | global_event_dispatcher: "moxie.core.EventTarget.instance.dispatchEvent" |
||
| 1537 | }; |
||
| 1538 | |||
| 1539 | // for backward compatibility |
||
| 1540 | // @deprecated Use `Env.os` instead |
||
| 1541 | Env.OS = Env.os; |
||
| 1542 | |||
| 1543 | if (MXI_DEBUG) { |
||
| 1544 | Env.debug = { |
||
| 1545 | runtime: true, |
||
| 1546 | events: false |
||
| 1547 | }; |
||
| 1548 | |||
| 1549 | Env.log = function() { |
||
| 1550 | |||
| 1551 | function logObj(data) { |
||
| 1552 | // TODO: this should recursively print out the object in a pretty way |
||
| 1553 | console.appendChild(document.createTextNode(data + "\n")); |
||
| 1554 | } |
||
| 1555 | |||
| 1556 | // if debugger present, IE8 might have window.console.log method, but not be able to apply on it (why...) |
||
| 1557 | if (window && window.console && window.console.log && window.console.log.apply) { |
||
| 1558 | window.console.log.apply(window.console, arguments); |
||
| 1559 | } else if (document) { |
||
| 1560 | var console = document.getElementById('moxie-console'); |
||
| 1561 | if (!console) { |
||
| 1562 | console = document.createElement('pre'); |
||
| 1563 | console.id = 'moxie-console'; |
||
| 1564 | //console.style.display = 'none'; |
||
| 1565 | document.body.appendChild(console); |
||
| 1566 | } |
||
| 1567 | |||
| 1568 | var data = arguments[0]; |
||
| 1569 | if (Basic.typeOf(data) === 'string') { |
||
| 1570 | data = Basic.sprintf.apply(this, arguments); |
||
| 1571 | } else if (Basic.inArray(Basic.typeOf(data), ['object', 'array']) !== -1) { |
||
| 1572 | logObj(data); |
||
| 1573 | return; |
||
| 1574 | } |
||
| 1575 | |||
| 1576 | console.appendChild(document.createTextNode(data + "\n")); |
||
| 1577 | } |
||
| 1578 | }; |
||
| 1579 | } |
||
| 1580 | |||
| 1581 | return Env; |
||
| 1582 | }); |
||
| 1583 | |||
| 1584 | // Included from: src/javascript/core/Exceptions.js |
||
| 1585 | |||
| 1586 | /** |
||
| 1587 | * Exceptions.js |
||
| 1588 | * |
||
| 1589 | * Copyright 2013, Moxiecode Systems AB |
||
| 1590 | * Released under GPL License. |
||
| 1591 | * |
||
| 1592 | * License: http://www.plupload.com/license |
||
| 1593 | * Contributing: http://www.plupload.com/contributing |
||
| 1594 | */ |
||
| 1595 | |||
| 1596 | define('moxie/core/Exceptions', [ |
||
| 1597 | 'moxie/core/utils/Basic' |
||
| 1598 | ], function(Basic) { |
||
| 1599 | |||
| 1600 | function _findKey(obj, value) { |
||
| 1601 | var key; |
||
| 1602 | for (key in obj) { |
||
| 1603 | if (obj[key] === value) { |
||
| 1604 | return key; |
||
| 1605 | } |
||
| 1606 | } |
||
| 1607 | return null; |
||
| 1608 | } |
||
| 1609 | |||
| 1610 | /** |
||
| 1611 | @class moxie/core/Exception |
||
| 1612 | */ |
||
| 1613 | return { |
||
| 1614 | RuntimeError: (function() { |
||
| 1615 | var namecodes = { |
||
| 1616 | NOT_INIT_ERR: 1, |
||
| 1617 | EXCEPTION_ERR: 3, |
||
| 1618 | NOT_SUPPORTED_ERR: 9, |
||
| 1619 | JS_ERR: 4 |
||
| 1620 | }; |
||
| 1621 | |||
| 1622 | function RuntimeError(code, message) { |
||
| 1623 | this.code = code; |
||
| 1624 | this.name = _findKey(namecodes, code); |
||
| 1625 | this.message = this.name + (message || ": RuntimeError " + this.code); |
||
| 1626 | } |
||
| 1627 | |||
| 1628 | Basic.extend(RuntimeError, namecodes); |
||
| 1629 | RuntimeError.prototype = Error.prototype; |
||
| 1630 | return RuntimeError; |
||
| 1631 | }()), |
||
| 1632 | |||
| 1633 | OperationNotAllowedException: (function() { |
||
| 1634 | |||
| 1635 | function OperationNotAllowedException(code) { |
||
| 1636 | this.code = code; |
||
| 1637 | this.name = 'OperationNotAllowedException'; |
||
| 1638 | } |
||
| 1639 | |||
| 1640 | Basic.extend(OperationNotAllowedException, { |
||
| 1641 | NOT_ALLOWED_ERR: 1 |
||
| 1642 | }); |
||
| 1643 | |||
| 1644 | OperationNotAllowedException.prototype = Error.prototype; |
||
| 1645 | |||
| 1646 | return OperationNotAllowedException; |
||
| 1647 | }()), |
||
| 1648 | |||
| 1649 | ImageError: (function() { |
||
| 1650 | var namecodes = { |
||
| 1651 | WRONG_FORMAT: 1, |
||
| 1652 | MAX_RESOLUTION_ERR: 2, |
||
| 1653 | INVALID_META_ERR: 3 |
||
| 1654 | }; |
||
| 1655 | |||
| 1656 | function ImageError(code) { |
||
| 1657 | this.code = code; |
||
| 1658 | this.name = _findKey(namecodes, code); |
||
| 1659 | this.message = this.name + ": ImageError " + this.code; |
||
| 1660 | } |
||
| 1661 | |||
| 1662 | Basic.extend(ImageError, namecodes); |
||
| 1663 | ImageError.prototype = Error.prototype; |
||
| 1664 | |||
| 1665 | return ImageError; |
||
| 1666 | }()), |
||
| 1667 | |||
| 1668 | FileException: (function() { |
||
| 1669 | var namecodes = { |
||
| 1670 | NOT_FOUND_ERR: 1, |
||
| 1671 | SECURITY_ERR: 2, |
||
| 1672 | ABORT_ERR: 3, |
||
| 1673 | NOT_READABLE_ERR: 4, |
||
| 1674 | ENCODING_ERR: 5, |
||
| 1675 | NO_MODIFICATION_ALLOWED_ERR: 6, |
||
| 1676 | INVALID_STATE_ERR: 7, |
||
| 1677 | SYNTAX_ERR: 8 |
||
| 1678 | }; |
||
| 1679 | |||
| 1680 | function FileException(code) { |
||
| 1681 | this.code = code; |
||
| 1682 | this.name = _findKey(namecodes, code); |
||
| 1683 | this.message = this.name + ": FileException " + this.code; |
||
| 1684 | } |
||
| 1685 | |||
| 1686 | Basic.extend(FileException, namecodes); |
||
| 1687 | FileException.prototype = Error.prototype; |
||
| 1688 | return FileException; |
||
| 1689 | }()), |
||
| 1690 | |||
| 1691 | DOMException: (function() { |
||
| 1692 | var namecodes = { |
||
| 1693 | INDEX_SIZE_ERR: 1, |
||
| 1694 | DOMSTRING_SIZE_ERR: 2, |
||
| 1695 | HIERARCHY_REQUEST_ERR: 3, |
||
| 1696 | WRONG_DOCUMENT_ERR: 4, |
||
| 1697 | INVALID_CHARACTER_ERR: 5, |
||
| 1698 | NO_DATA_ALLOWED_ERR: 6, |
||
| 1699 | NO_MODIFICATION_ALLOWED_ERR: 7, |
||
| 1700 | NOT_FOUND_ERR: 8, |
||
| 1701 | NOT_SUPPORTED_ERR: 9, |
||
| 1702 | INUSE_ATTRIBUTE_ERR: 10, |
||
| 1703 | INVALID_STATE_ERR: 11, |
||
| 1704 | SYNTAX_ERR: 12, |
||
| 1705 | INVALID_MODIFICATION_ERR: 13, |
||
| 1706 | NAMESPACE_ERR: 14, |
||
| 1707 | INVALID_ACCESS_ERR: 15, |
||
| 1708 | VALIDATION_ERR: 16, |
||
| 1709 | TYPE_MISMATCH_ERR: 17, |
||
| 1710 | SECURITY_ERR: 18, |
||
| 1711 | NETWORK_ERR: 19, |
||
| 1712 | ABORT_ERR: 20, |
||
| 1713 | URL_MISMATCH_ERR: 21, |
||
| 1714 | QUOTA_EXCEEDED_ERR: 22, |
||
| 1715 | TIMEOUT_ERR: 23, |
||
| 1716 | INVALID_NODE_TYPE_ERR: 24, |
||
| 1717 | DATA_CLONE_ERR: 25 |
||
| 1718 | }; |
||
| 1719 | |||
| 1720 | function DOMException(code) { |
||
| 1721 | this.code = code; |
||
| 1722 | this.name = _findKey(namecodes, code); |
||
| 1723 | this.message = this.name + ": DOMException " + this.code; |
||
| 1724 | } |
||
| 1725 | |||
| 1726 | Basic.extend(DOMException, namecodes); |
||
| 1727 | DOMException.prototype = Error.prototype; |
||
| 1728 | return DOMException; |
||
| 1729 | }()), |
||
| 1730 | |||
| 1731 | EventException: (function() { |
||
| 1732 | function EventException(code) { |
||
| 1733 | this.code = code; |
||
| 1734 | this.name = 'EventException'; |
||
| 1735 | } |
||
| 1736 | |||
| 1737 | Basic.extend(EventException, { |
||
| 1738 | UNSPECIFIED_EVENT_TYPE_ERR: 0 |
||
| 1739 | }); |
||
| 1740 | |||
| 1741 | EventException.prototype = Error.prototype; |
||
| 1742 | |||
| 1743 | return EventException; |
||
| 1744 | }()) |
||
| 1745 | }; |
||
| 1746 | }); |
||
| 1747 | |||
| 1748 | // Included from: src/javascript/core/utils/Dom.js |
||
| 1749 | |||
| 1750 | /** |
||
| 1751 | * Dom.js |
||
| 1752 | * |
||
| 1753 | * Copyright 2013, Moxiecode Systems AB |
||
| 1754 | * Released under GPL License. |
||
| 1755 | * |
||
| 1756 | * License: http://www.plupload.com/license |
||
| 1757 | * Contributing: http://www.plupload.com/contributing |
||
| 1758 | */ |
||
| 1759 | |||
| 1760 | /** |
||
| 1761 | @class moxie/core/utils/Dom |
||
| 1762 | @public |
||
| 1763 | @static |
||
| 1764 | */ |
||
| 1765 | |||
| 1766 | define('moxie/core/utils/Dom', ['moxie/core/utils/Env'], function(Env) { |
||
| 1767 | |||
| 1768 | /** |
||
| 1769 | Get DOM Element by it's id. |
||
| 1770 | |||
| 1771 | @method get |
||
| 1772 | @param {String} id Identifier of the DOM Element |
||
| 1773 | @return {DOMElement} |
||
| 1774 | */ |
||
| 1775 | var get = function(id) { |
||
| 1776 | if (typeof id !== 'string') { |
||
| 1777 | return id; |
||
| 1778 | } |
||
| 1779 | return document.getElementById(id); |
||
| 1780 | }; |
||
| 1781 | |||
| 1782 | /** |
||
| 1783 | Checks if specified DOM element has specified class. |
||
| 1784 | |||
| 1785 | @method hasClass |
||
| 1786 | @static |
||
| 1787 | @param {Object} obj DOM element like object to add handler to. |
||
| 1788 | @param {String} name Class name |
||
| 1789 | */ |
||
| 1790 | var hasClass = function(obj, name) { |
||
| 1791 | if (!obj.className) { |
||
| 1792 | return false; |
||
| 1793 | } |
||
| 1794 | |||
| 1795 | var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)"); |
||
| 1796 | return regExp.test(obj.className); |
||
| 1797 | }; |
||
| 1798 | |||
| 1799 | /** |
||
| 1800 | Adds specified className to specified DOM element. |
||
| 1801 | |||
| 1802 | @method addClass |
||
| 1803 | @static |
||
| 1804 | @param {Object} obj DOM element like object to add handler to. |
||
| 1805 | @param {String} name Class name |
||
| 1806 | */ |
||
| 1807 | var addClass = function(obj, name) { |
||
| 1808 | if (!hasClass(obj, name)) { |
||
| 1809 | obj.className = !obj.className ? name : obj.className.replace(/\s+$/, '') + ' ' + name; |
||
| 1810 | } |
||
| 1811 | }; |
||
| 1812 | |||
| 1813 | /** |
||
| 1814 | Removes specified className from specified DOM element. |
||
| 1815 | |||
| 1816 | @method removeClass |
||
| 1817 | @static |
||
| 1818 | @param {Object} obj DOM element like object to add handler to. |
||
| 1819 | @param {String} name Class name |
||
| 1820 | */ |
||
| 1821 | var removeClass = function(obj, name) { |
||
| 1822 | if (obj.className) { |
||
| 1823 | var regExp = new RegExp("(^|\\s+)"+name+"(\\s+|$)"); |
||
| 1824 | obj.className = obj.className.replace(regExp, function($0, $1, $2) { |
||
| 1825 | return $1 === ' ' && $2 === ' ' ? ' ' : ''; |
||
| 1826 | }); |
||
| 1827 | } |
||
| 1828 | }; |
||
| 1829 | |||
| 1830 | /** |
||
| 1831 | Returns a given computed style of a DOM element. |
||
| 1832 | |||
| 1833 | @method getStyle |
||
| 1834 | @static |
||
| 1835 | @param {Object} obj DOM element like object. |
||
| 1836 | @param {String} name Style you want to get from the DOM element |
||
| 1837 | */ |
||
| 1838 | var getStyle = function(obj, name) { |
||
| 1839 | if (obj.currentStyle) { |
||
| 1840 | return obj.currentStyle[name]; |
||
| 1841 | } else if (window.getComputedStyle) { |
||
| 1842 | return window.getComputedStyle(obj, null)[name]; |
||
| 1843 | } |
||
| 1844 | }; |
||
| 1845 | |||
| 1846 | |||
| 1847 | /** |
||
| 1848 | Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields. |
||
| 1849 | |||
| 1850 | @method getPos |
||
| 1851 | @static |
||
| 1852 | @param {Element} node HTML element or element id to get x, y position from. |
||
| 1853 | @param {Element} root Optional root element to stop calculations at. |
||
| 1854 | @return {object} Absolute position of the specified element object with x, y fields. |
||
| 1855 | */ |
||
| 1856 | var getPos = function(node, root) { |
||
| 1857 | var x = 0, y = 0, parent, doc = document, nodeRect, rootRect; |
||
| 1858 | |||
| 1859 | node = node; |
||
| 1860 | root = root || doc.body; |
||
| 1861 | |||
| 1862 | // Returns the x, y cordinate for an element on IE 6 and IE 7 |
||
| 1863 | function getIEPos(node) { |
||
| 1864 | var bodyElm, rect, x = 0, y = 0; |
||
| 1865 | |||
| 1866 | if (node) { |
||
| 1867 | rect = node.getBoundingClientRect(); |
||
| 1868 | bodyElm = doc.compatMode === "CSS1Compat" ? doc.documentElement : doc.body; |
||
| 1869 | x = rect.left + bodyElm.scrollLeft; |
||
| 1870 | y = rect.top + bodyElm.scrollTop; |
||
| 1871 | } |
||
| 1872 | |||
| 1873 | return { |
||
| 1874 | x : x, |
||
| 1875 | y : y |
||
| 1876 | }; |
||
| 1877 | } |
||
| 1878 | |||
| 1879 | // Use getBoundingClientRect on IE 6 and IE 7 but not on IE 8 in standards mode |
||
| 1880 | if (node && node.getBoundingClientRect && Env.browser === 'IE' && (!doc.documentMode || doc.documentMode < 8)) { |
||
| 1881 | nodeRect = getIEPos(node); |
||
| 1882 | rootRect = getIEPos(root); |
||
| 1883 | |||
| 1884 | return { |
||
| 1885 | x : nodeRect.x - rootRect.x, |
||
| 1886 | y : nodeRect.y - rootRect.y |
||
| 1887 | }; |
||
| 1888 | } |
||
| 1889 | |||
| 1890 | parent = node; |
||
| 1891 | while (parent && parent != root && parent.nodeType) { |
||
| 1892 | x += parent.offsetLeft || 0; |
||
| 1893 | y += parent.offsetTop || 0; |
||
| 1894 | parent = parent.offsetParent; |
||
| 1895 | } |
||
| 1896 | |||
| 1897 | parent = node.parentNode; |
||
| 1898 | while (parent && parent != root && parent.nodeType) { |
||
| 1899 | x -= parent.scrollLeft || 0; |
||
| 1900 | y -= parent.scrollTop || 0; |
||
| 1901 | parent = parent.parentNode; |
||
| 1902 | } |
||
| 1903 | |||
| 1904 | return { |
||
| 1905 | x : x, |
||
| 1906 | y : y |
||
| 1907 | }; |
||
| 1908 | }; |
||
| 1909 | |||
| 1910 | /** |
||
| 1911 | Returns the size of the specified node in pixels. |
||
| 1912 | |||
| 1913 | @method getSize |
||
| 1914 | @static |
||
| 1915 | @param {Node} node Node to get the size of. |
||
| 1916 | @return {Object} Object with a w and h property. |
||
| 1917 | */ |
||
| 1918 | var getSize = function(node) { |
||
| 1919 | return { |
||
| 1920 | w : node.offsetWidth || node.clientWidth, |
||
| 1921 | h : node.offsetHeight || node.clientHeight |
||
| 1922 | }; |
||
| 1923 | }; |
||
| 1924 | |||
| 1925 | return { |
||
| 1926 | get: get, |
||
| 1927 | hasClass: hasClass, |
||
| 1928 | addClass: addClass, |
||
| 1929 | removeClass: removeClass, |
||
| 1930 | getStyle: getStyle, |
||
| 1931 | getPos: getPos, |
||
| 1932 | getSize: getSize |
||
| 1933 | }; |
||
| 1934 | }); |
||
| 1935 | |||
| 1936 | // Included from: src/javascript/core/EventTarget.js |
||
| 1937 | |||
| 1938 | /** |
||
| 1939 | * EventTarget.js |
||
| 1940 | * |
||
| 1941 | * Copyright 2013, Moxiecode Systems AB |
||
| 1942 | * Released under GPL License. |
||
| 1943 | * |
||
| 1944 | * License: http://www.plupload.com/license |
||
| 1945 | * Contributing: http://www.plupload.com/contributing |
||
| 1946 | */ |
||
| 1947 | |||
| 1948 | define('moxie/core/EventTarget', [ |
||
| 1949 | 'moxie/core/utils/Env', |
||
| 1950 | 'moxie/core/Exceptions', |
||
| 1951 | 'moxie/core/utils/Basic' |
||
| 1952 | ], function(Env, x, Basic) { |
||
| 1953 | |||
| 1954 | // hash of event listeners by object uid |
||
| 1955 | var eventpool = {}; |
||
| 1956 | |||
| 1957 | /** |
||
| 1958 | Parent object for all event dispatching components and objects |
||
| 1959 | |||
| 1960 | @class moxie/core/EventTarget |
||
| 1961 | @constructor EventTarget |
||
| 1962 | */ |
||
| 1963 | function EventTarget() { |
||
| 1964 | /** |
||
| 1965 | Unique id of the event dispatcher, usually overriden by children |
||
| 1966 | |||
| 1967 | @property uid |
||
| 1968 | @type String |
||
| 1969 | */ |
||
| 1970 | this.uid = Basic.guid(); |
||
| 1971 | } |
||
| 1972 | |||
| 1973 | |||
| 1974 | Basic.extend(EventTarget.prototype, { |
||
| 1975 | |||
| 1976 | /** |
||
| 1977 | Can be called from within a child in order to acquire uniqie id in automated manner |
||
| 1978 | |||
| 1979 | @method init |
||
| 1980 | */ |
||
| 1981 | init: function() { |
||
| 1982 | if (!this.uid) { |
||
| 1983 | this.uid = Basic.guid('uid_'); |
||
| 1984 | } |
||
| 1985 | }, |
||
| 1986 | |||
| 1987 | /** |
||
| 1988 | Register a handler to a specific event dispatched by the object |
||
| 1989 | |||
| 1990 | @method addEventListener |
||
| 1991 | @param {String} type Type or basically a name of the event to subscribe to |
||
| 1992 | @param {Function} fn Callback function that will be called when event happens |
||
| 1993 | @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first |
||
| 1994 | @param {Object} [scope=this] A scope to invoke event handler in |
||
| 1995 | */ |
||
| 1996 | addEventListener: function(type, fn, priority, scope) { |
||
| 1997 | var self = this, list; |
||
| 1998 | |||
| 1999 | // without uid no event handlers can be added, so make sure we got one |
||
| 2000 | if (!this.hasOwnProperty('uid')) { |
||
| 2001 | this.uid = Basic.guid('uid_'); |
||
| 2002 | } |
||
| 2003 | |||
| 2004 | type = Basic.trim(type); |
||
| 2005 | |||
| 2006 | if (/\s/.test(type)) { |
||
| 2007 | // multiple event types were passed for one handler |
||
| 2008 | Basic.each(type.split(/\s+/), function(type) { |
||
| 2009 | self.addEventListener(type, fn, priority, scope); |
||
| 2010 | }); |
||
| 2011 | return; |
||
| 2012 | } |
||
| 2013 | |||
| 2014 | type = type.toLowerCase(); |
||
| 2015 | priority = parseInt(priority, 10) || 0; |
||
| 2016 | |||
| 2017 | list = eventpool[this.uid] && eventpool[this.uid][type] || []; |
||
| 2018 | list.push({fn : fn, priority : priority, scope : scope || this}); |
||
| 2019 | |||
| 2020 | if (!eventpool[this.uid]) { |
||
| 2021 | eventpool[this.uid] = {}; |
||
| 2022 | } |
||
| 2023 | eventpool[this.uid][type] = list; |
||
| 2024 | }, |
||
| 2025 | |||
| 2026 | /** |
||
| 2027 | Check if any handlers were registered to the specified event |
||
| 2028 | |||
| 2029 | @method hasEventListener |
||
| 2030 | @param {String} [type] Type or basically a name of the event to check |
||
| 2031 | @return {Mixed} Returns a handler if it was found and false, if - not |
||
| 2032 | */ |
||
| 2033 | hasEventListener: function(type) { |
||
| 2034 | var list; |
||
| 2035 | if (type) { |
||
| 2036 | type = type.toLowerCase(); |
||
| 2037 | list = eventpool[this.uid] && eventpool[this.uid][type]; |
||
| 2038 | } else { |
||
| 2039 | list = eventpool[this.uid]; |
||
| 2040 | } |
||
| 2041 | return list ? list : false; |
||
| 2042 | }, |
||
| 2043 | |||
| 2044 | /** |
||
| 2045 | Unregister the handler from the event, or if former was not specified - unregister all handlers |
||
| 2046 | |||
| 2047 | @method removeEventListener |
||
| 2048 | @param {String} type Type or basically a name of the event |
||
| 2049 | @param {Function} [fn] Handler to unregister |
||
| 2050 | */ |
||
| 2051 | removeEventListener: function(type, fn) { |
||
| 2052 | var self = this, list, i; |
||
| 2053 | |||
| 2054 | type = type.toLowerCase(); |
||
| 2055 | |||
| 2056 | if (/\s/.test(type)) { |
||
| 2057 | // multiple event types were passed for one handler |
||
| 2058 | Basic.each(type.split(/\s+/), function(type) { |
||
| 2059 | self.removeEventListener(type, fn); |
||
| 2060 | }); |
||
| 2061 | return; |
||
| 2062 | } |
||
| 2063 | |||
| 2064 | list = eventpool[this.uid] && eventpool[this.uid][type]; |
||
| 2065 | |||
| 2066 | if (list) { |
||
| 2067 | if (fn) { |
||
| 2068 | for (i = list.length - 1; i >= 0; i--) { |
||
| 2069 | if (list[i].fn === fn) { |
||
| 2070 | list.splice(i, 1); |
||
| 2071 | break; |
||
| 2072 | } |
||
| 2073 | } |
||
| 2074 | } else { |
||
| 2075 | list = []; |
||
| 2076 | } |
||
| 2077 | |||
| 2078 | // delete event list if it has become empty |
||
| 2079 | if (!list.length) { |
||
| 2080 | delete eventpool[this.uid][type]; |
||
| 2081 | |||
| 2082 | // and object specific entry in a hash if it has no more listeners attached |
||
| 2083 | if (Basic.isEmptyObj(eventpool[this.uid])) { |
||
| 2084 | delete eventpool[this.uid]; |
||
| 2085 | } |
||
| 2086 | } |
||
| 2087 | } |
||
| 2088 | }, |
||
| 2089 | |||
| 2090 | /** |
||
| 2091 | Remove all event handlers from the object |
||
| 2092 | |||
| 2093 | @method removeAllEventListeners |
||
| 2094 | */ |
||
| 2095 | removeAllEventListeners: function() { |
||
| 2096 | if (eventpool[this.uid]) { |
||
| 2097 | delete eventpool[this.uid]; |
||
| 2098 | } |
||
| 2099 | }, |
||
| 2100 | |||
| 2101 | /** |
||
| 2102 | Dispatch the event |
||
| 2103 | |||
| 2104 | @method dispatchEvent |
||
| 2105 | @param {String/Object} Type of event or event object to dispatch |
||
| 2106 | @param {Mixed} [...] Variable number of arguments to be passed to a handlers |
||
| 2107 | @return {Boolean} true by default and false if any handler returned false |
||
| 2108 | */ |
||
| 2109 | dispatchEvent: function(type) { |
||
| 2110 | var uid, list, args, tmpEvt, evt = {}, result = true, undef; |
||
| 2111 | |||
| 2112 | if (Basic.typeOf(type) !== 'string') { |
||
| 2113 | // we can't use original object directly (because of Silverlight) |
||
| 2114 | tmpEvt = type; |
||
| 2115 | |||
| 2116 | if (Basic.typeOf(tmpEvt.type) === 'string') { |
||
| 2117 | type = tmpEvt.type; |
||
| 2118 | |||
| 2119 | if (tmpEvt.total !== undef && tmpEvt.loaded !== undef) { // progress event |
||
| 2120 | evt.total = tmpEvt.total; |
||
| 2121 | evt.loaded = tmpEvt.loaded; |
||
| 2122 | } |
||
| 2123 | evt.async = tmpEvt.async || false; |
||
| 2124 | } else { |
||
| 2125 | throw new x.EventException(x.EventException.UNSPECIFIED_EVENT_TYPE_ERR); |
||
| 2126 | } |
||
| 2127 | } |
||
| 2128 | |||
| 2129 | // check if event is meant to be dispatched on an object having specific uid |
||
| 2130 | if (type.indexOf('::') !== -1) { |
||
| 2131 | (function(arr) { |
||
| 2132 | uid = arr[0]; |
||
| 2133 | type = arr[1]; |
||
| 2134 | }(type.split('::'))); |
||
| 2135 | } else { |
||
| 2136 | uid = this.uid; |
||
| 2137 | } |
||
| 2138 | |||
| 2139 | type = type.toLowerCase(); |
||
| 2140 | |||
| 2141 | list = eventpool[uid] && eventpool[uid][type]; |
||
| 2142 | |||
| 2143 | if (list) { |
||
| 2144 | // sort event list by prority |
||
| 2145 | list.sort(function(a, b) { return b.priority - a.priority; }); |
||
| 2146 | |||
| 2147 | args = [].slice.call(arguments); |
||
| 2148 | |||
| 2149 | // first argument will be pseudo-event object |
||
| 2150 | args.shift(); |
||
| 2151 | evt.type = type; |
||
| 2152 | args.unshift(evt); |
||
| 2153 | |||
| 2154 | if (MXI_DEBUG && Env.debug.events) { |
||
| 2155 | Env.log("%cEvent '%s' fired on %s", 'color: #999;', evt.type, (this.ctorName ? this.ctorName + '::' : '') + uid); |
||
| 2156 | } |
||
| 2157 | |||
| 2158 | // Dispatch event to all listeners |
||
| 2159 | var queue = []; |
||
| 2160 | Basic.each(list, function(handler) { |
||
| 2161 | // explicitly set the target, otherwise events fired from shims do not get it |
||
| 2162 | args[0].target = handler.scope; |
||
| 2163 | // if event is marked as async, detach the handler |
||
| 2164 | if (evt.async) { |
||
| 2165 | queue.push(function(cb) { |
||
| 2166 | setTimeout(function() { |
||
| 2167 | cb(handler.fn.apply(handler.scope, args) === false); |
||
| 2168 | }, 1); |
||
| 2169 | }); |
||
| 2170 | } else { |
||
| 2171 | queue.push(function(cb) { |
||
| 2172 | cb(handler.fn.apply(handler.scope, args) === false); // if handler returns false stop propagation |
||
| 2173 | }); |
||
| 2174 | } |
||
| 2175 | }); |
||
| 2176 | if (queue.length) { |
||
| 2177 | Basic.inSeries(queue, function(err) { |
||
| 2178 | result = !err; |
||
| 2179 | }); |
||
| 2180 | } |
||
| 2181 | } |
||
| 2182 | return result; |
||
| 2183 | }, |
||
| 2184 | |||
| 2185 | /** |
||
| 2186 | Register a handler to the event type that will run only once |
||
| 2187 | |||
| 2188 | @method bindOnce |
||
| 2189 | @since >1.4.1 |
||
| 2190 | @param {String} type Type or basically a name of the event to subscribe to |
||
| 2191 | @param {Function} fn Callback function that will be called when event happens |
||
| 2192 | @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first |
||
| 2193 | @param {Object} [scope=this] A scope to invoke event handler in |
||
| 2194 | */ |
||
| 2195 | bindOnce: function(type, fn, priority, scope) { |
||
| 2196 | var self = this; |
||
| 2197 | self.bind.call(this, type, function cb() { |
||
| 2198 | self.unbind(type, cb); |
||
| 2199 | return fn.apply(this, arguments); |
||
| 2200 | }, priority, scope); |
||
| 2201 | }, |
||
| 2202 | |||
| 2203 | /** |
||
| 2204 | Alias for addEventListener |
||
| 2205 | |||
| 2206 | @method bind |
||
| 2207 | @protected |
||
| 2208 | */ |
||
| 2209 | bind: function() { |
||
| 2210 | this.addEventListener.apply(this, arguments); |
||
| 2211 | }, |
||
| 2212 | |||
| 2213 | /** |
||
| 2214 | Alias for removeEventListener |
||
| 2215 | |||
| 2216 | @method unbind |
||
| 2217 | @protected |
||
| 2218 | */ |
||
| 2219 | unbind: function() { |
||
| 2220 | this.removeEventListener.apply(this, arguments); |
||
| 2221 | }, |
||
| 2222 | |||
| 2223 | /** |
||
| 2224 | Alias for removeAllEventListeners |
||
| 2225 | |||
| 2226 | @method unbindAll |
||
| 2227 | @protected |
||
| 2228 | */ |
||
| 2229 | unbindAll: function() { |
||
| 2230 | this.removeAllEventListeners.apply(this, arguments); |
||
| 2231 | }, |
||
| 2232 | |||
| 2233 | /** |
||
| 2234 | Alias for dispatchEvent |
||
| 2235 | |||
| 2236 | @method trigger |
||
| 2237 | @protected |
||
| 2238 | */ |
||
| 2239 | trigger: function() { |
||
| 2240 | return this.dispatchEvent.apply(this, arguments); |
||
| 2241 | }, |
||
| 2242 | |||
| 2243 | |||
| 2244 | /** |
||
| 2245 | Handle properties of on[event] type. |
||
| 2246 | |||
| 2247 | @method handleEventProps |
||
| 2248 | @private |
||
| 2249 | */ |
||
| 2250 | handleEventProps: function(dispatches) { |
||
| 2251 | var self = this; |
||
| 2252 | |||
| 2253 | this.bind(dispatches.join(' '), function(e) { |
||
| 2254 | var prop = 'on' + e.type.toLowerCase(); |
||
| 2255 | if (Basic.typeOf(this[prop]) === 'function') { |
||
| 2256 | this[prop].apply(this, arguments); |
||
| 2257 | } |
||
| 2258 | }); |
||
| 2259 | |||
| 2260 | // object must have defined event properties, even if it doesn't make use of them |
||
| 2261 | Basic.each(dispatches, function(prop) { |
||
| 2262 | prop = 'on' + prop.toLowerCase(prop); |
||
| 2263 | if (Basic.typeOf(self[prop]) === 'undefined') { |
||
| 2264 | self[prop] = null; |
||
| 2265 | } |
||
| 2266 | }); |
||
| 2267 | } |
||
| 2268 | |||
| 2269 | }); |
||
| 2270 | |||
| 2271 | |||
| 2272 | EventTarget.instance = new EventTarget(); |
||
| 2273 | |||
| 2274 | return EventTarget; |
||
| 2275 | }); |
||
| 2276 | |||
| 2277 | // Included from: src/javascript/runtime/Runtime.js |
||
| 2278 | |||
| 2279 | /** |
||
| 2280 | * Runtime.js |
||
| 2281 | * |
||
| 2282 | * Copyright 2013, Moxiecode Systems AB |
||
| 2283 | * Released under GPL License. |
||
| 2284 | * |
||
| 2285 | * License: http://www.plupload.com/license |
||
| 2286 | * Contributing: http://www.plupload.com/contributing |
||
| 2287 | */ |
||
| 2288 | |||
| 2289 | define('moxie/runtime/Runtime', [ |
||
| 2290 | "moxie/core/utils/Env", |
||
| 2291 | "moxie/core/utils/Basic", |
||
| 2292 | "moxie/core/utils/Dom", |
||
| 2293 | "moxie/core/EventTarget" |
||
| 2294 | ], function(Env, Basic, Dom, EventTarget) { |
||
| 2295 | var runtimeConstructors = {}, runtimes = {}; |
||
| 2296 | |||
| 2297 | /** |
||
| 2298 | Common set of methods and properties for every runtime instance |
||
| 2299 | |||
| 2300 | @class moxie/runtime/Runtime |
||
| 2301 | |||
| 2302 | @param {Object} options |
||
| 2303 | @param {String} type Sanitized name of the runtime |
||
| 2304 | @param {Object} [caps] Set of capabilities that differentiate specified runtime |
||
| 2305 | @param {Object} [modeCaps] Set of capabilities that do require specific operational mode |
||
| 2306 | @param {String} [preferredMode='browser'] Preferred operational mode to choose if no required capabilities were requested |
||
| 2307 | */ |
||
| 2308 | function Runtime(options, type, caps, modeCaps, preferredMode) { |
||
| 2309 | /** |
||
| 2310 | Dispatched when runtime is initialized and ready. |
||
| 2311 | Results in RuntimeInit on a connected component. |
||
| 2312 | |||
| 2313 | @event Init |
||
| 2314 | */ |
||
| 2315 | |||
| 2316 | /** |
||
| 2317 | Dispatched when runtime fails to initialize. |
||
| 2318 | Results in RuntimeError on a connected component. |
||
| 2319 | |||
| 2320 | @event Error |
||
| 2321 | */ |
||
| 2322 | |||
| 2323 | var self = this |
||
| 2324 | , _shim |
||
| 2325 | , _uid = Basic.guid(type + '_') |
||
| 2326 | , defaultMode = preferredMode || 'browser' |
||
| 2327 | ; |
||
| 2328 | |||
| 2329 | options = options || {}; |
||
| 2330 | |||
| 2331 | // register runtime in private hash |
||
| 2332 | runtimes[_uid] = this; |
||
| 2333 | |||
| 2334 | /** |
||
| 2335 | Default set of capabilities, which can be redifined later by specific runtime |
||
| 2336 | |||
| 2337 | @private |
||
| 2338 | @property caps |
||
| 2339 | @type Object |
||
| 2340 | */ |
||
| 2341 | caps = Basic.extend({ |
||
| 2342 | // Runtime can: |
||
| 2343 | // provide access to raw binary data of the file |
||
| 2344 | access_binary: false, |
||
| 2345 | // provide access to raw binary data of the image (image extension is optional) |
||
| 2346 | access_image_binary: false, |
||
| 2347 | // display binary data as thumbs for example |
||
| 2348 | display_media: false, |
||
| 2349 | // make cross-domain requests |
||
| 2350 | do_cors: false, |
||
| 2351 | // accept files dragged and dropped from the desktop |
||
| 2352 | drag_and_drop: false, |
||
| 2353 | // filter files in selection dialog by their extensions |
||
| 2354 | filter_by_extension: true, |
||
| 2355 | // resize image (and manipulate it raw data of any file in general) |
||
| 2356 | resize_image: false, |
||
| 2357 | // periodically report how many bytes of total in the file were uploaded (loaded) |
||
| 2358 | report_upload_progress: false, |
||
| 2359 | // provide access to the headers of http response |
||
| 2360 | return_response_headers: false, |
||
| 2361 | // support response of specific type, which should be passed as an argument |
||
| 2362 | // e.g. runtime.can('return_response_type', 'blob') |
||
| 2363 | return_response_type: false, |
||
| 2364 | // return http status code of the response |
||
| 2365 | return_status_code: true, |
||
| 2366 | // send custom http header with the request |
||
| 2367 | send_custom_headers: false, |
||
| 2368 | // pick up the files from a dialog |
||
| 2369 | select_file: false, |
||
| 2370 | // select whole folder in file browse dialog |
||
| 2371 | select_folder: false, |
||
| 2372 | // select multiple files at once in file browse dialog |
||
| 2373 | select_multiple: true, |
||
| 2374 | // send raw binary data, that is generated after image resizing or manipulation of other kind |
||
| 2375 | send_binary_string: false, |
||
| 2376 | // send cookies with http request and therefore retain session |
||
| 2377 | send_browser_cookies: true, |
||
| 2378 | // send data formatted as multipart/form-data |
||
| 2379 | send_multipart: true, |
||
| 2380 | // slice the file or blob to smaller parts |
||
| 2381 | slice_blob: false, |
||
| 2382 | // upload file without preloading it to memory, stream it out directly from disk |
||
| 2383 | stream_upload: false, |
||
| 2384 | // programmatically trigger file browse dialog |
||
| 2385 | summon_file_dialog: false, |
||
| 2386 | // upload file of specific size, size should be passed as argument |
||
| 2387 | // e.g. runtime.can('upload_filesize', '500mb') |
||
| 2388 | upload_filesize: true, |
||
| 2389 | // initiate http request with specific http method, method should be passed as argument |
||
| 2390 | // e.g. runtime.can('use_http_method', 'put') |
||
| 2391 | use_http_method: true |
||
| 2392 | }, caps); |
||
| 2393 | |||
| 2394 | |||
| 2395 | // default to the mode that is compatible with preferred caps |
||
| 2396 | if (options.preferred_caps) { |
||
| 2397 | defaultMode = Runtime.getMode(modeCaps, options.preferred_caps, defaultMode); |
||
| 2398 | } |
||
| 2399 | |||
| 2400 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2401 | Env.log("\tdefault mode: %s", defaultMode); |
||
| 2402 | } |
||
| 2403 | |||
| 2404 | // small extension factory here (is meant to be extended with actual extensions constructors) |
||
| 2405 | _shim = (function() { |
||
| 2406 | var objpool = {}; |
||
| 2407 | return { |
||
| 2408 | exec: function(uid, comp, fn, args) { |
||
| 2409 | if (_shim[comp]) { |
||
| 2410 | if (!objpool[uid]) { |
||
| 2411 | objpool[uid] = { |
||
| 2412 | context: this, |
||
| 2413 | instance: new _shim[comp]() |
||
| 2414 | }; |
||
| 2415 | } |
||
| 2416 | if (objpool[uid].instance[fn]) { |
||
| 2417 | return objpool[uid].instance[fn].apply(this, args); |
||
| 2418 | } |
||
| 2419 | } |
||
| 2420 | }, |
||
| 2421 | |||
| 2422 | removeInstance: function(uid) { |
||
| 2423 | delete objpool[uid]; |
||
| 2424 | }, |
||
| 2425 | |||
| 2426 | removeAllInstances: function() { |
||
| 2427 | var self = this; |
||
| 2428 | Basic.each(objpool, function(obj, uid) { |
||
| 2429 | if (Basic.typeOf(obj.instance.destroy) === 'function') { |
||
| 2430 | obj.instance.destroy.call(obj.context); |
||
| 2431 | } |
||
| 2432 | self.removeInstance(uid); |
||
| 2433 | }); |
||
| 2434 | } |
||
| 2435 | }; |
||
| 2436 | }()); |
||
| 2437 | |||
| 2438 | |||
| 2439 | // public methods |
||
| 2440 | Basic.extend(this, { |
||
| 2441 | /** |
||
| 2442 | Specifies whether runtime instance was initialized or not |
||
| 2443 | |||
| 2444 | @property initialized |
||
| 2445 | @type {Boolean} |
||
| 2446 | @default false |
||
| 2447 | */ |
||
| 2448 | initialized: false, // shims require this flag to stop initialization retries |
||
| 2449 | |||
| 2450 | /** |
||
| 2451 | Unique ID of the runtime |
||
| 2452 | |||
| 2453 | @property uid |
||
| 2454 | @type {String} |
||
| 2455 | */ |
||
| 2456 | uid: _uid, |
||
| 2457 | |||
| 2458 | /** |
||
| 2459 | Runtime type (e.g. flash, html5, etc) |
||
| 2460 | |||
| 2461 | @property type |
||
| 2462 | @type {String} |
||
| 2463 | */ |
||
| 2464 | type: type, |
||
| 2465 | |||
| 2466 | /** |
||
| 2467 | Runtime (not native one) may operate in browser or client mode. |
||
| 2468 | |||
| 2469 | @property mode |
||
| 2470 | @private |
||
| 2471 | @type {String|Boolean} current mode or false, if none possible |
||
| 2472 | */ |
||
| 2473 | mode: Runtime.getMode(modeCaps, (options.required_caps), defaultMode), |
||
| 2474 | |||
| 2475 | /** |
||
| 2476 | id of the DOM container for the runtime (if available) |
||
| 2477 | |||
| 2478 | @property shimid |
||
| 2479 | @type {String} |
||
| 2480 | */ |
||
| 2481 | shimid: _uid + '_container', |
||
| 2482 | |||
| 2483 | /** |
||
| 2484 | Number of connected clients. If equal to zero, runtime can be destroyed |
||
| 2485 | |||
| 2486 | @property clients |
||
| 2487 | @type {Number} |
||
| 2488 | */ |
||
| 2489 | clients: 0, |
||
| 2490 | |||
| 2491 | /** |
||
| 2492 | Runtime initialization options |
||
| 2493 | |||
| 2494 | @property options |
||
| 2495 | @type {Object} |
||
| 2496 | */ |
||
| 2497 | options: options, |
||
| 2498 | |||
| 2499 | /** |
||
| 2500 | Checks if the runtime has specific capability |
||
| 2501 | |||
| 2502 | @method can |
||
| 2503 | @param {String} cap Name of capability to check |
||
| 2504 | @param {Mixed} [value] If passed, capability should somehow correlate to the value |
||
| 2505 | @param {Object} [refCaps] Set of capabilities to check the specified cap against (defaults to internal set) |
||
| 2506 | @return {Boolean} true if runtime has such capability and false, if - not |
||
| 2507 | */ |
||
| 2508 | can: function(cap, value) { |
||
| 2509 | var refCaps = arguments[2] || caps; |
||
| 2510 | |||
| 2511 | // if cap var is a comma-separated list of caps, convert it to object (key/value) |
||
| 2512 | if (Basic.typeOf(cap) === 'string' && Basic.typeOf(value) === 'undefined') { |
||
| 2513 | cap = Runtime.parseCaps(cap); |
||
| 2514 | } |
||
| 2515 | |||
| 2516 | if (Basic.typeOf(cap) === 'object') { |
||
| 2517 | for (var key in cap) { |
||
| 2518 | if (!this.can(key, cap[key], refCaps)) { |
||
| 2519 | return false; |
||
| 2520 | } |
||
| 2521 | } |
||
| 2522 | return true; |
||
| 2523 | } |
||
| 2524 | |||
| 2525 | // check the individual cap |
||
| 2526 | if (Basic.typeOf(refCaps[cap]) === 'function') { |
||
| 2527 | return refCaps[cap].call(this, value); |
||
| 2528 | } else { |
||
| 2529 | return (value === refCaps[cap]); |
||
| 2530 | } |
||
| 2531 | }, |
||
| 2532 | |||
| 2533 | /** |
||
| 2534 | Returns container for the runtime as DOM element |
||
| 2535 | |||
| 2536 | @method getShimContainer |
||
| 2537 | @return {DOMElement} |
||
| 2538 | */ |
||
| 2539 | getShimContainer: function() { |
||
| 2540 | var container, shimContainer = Dom.get(this.shimid); |
||
| 2541 | |||
| 2542 | // if no container for shim, create one |
||
| 2543 | if (!shimContainer) { |
||
| 2544 | container = Dom.get(this.options.container) || document.body; |
||
| 2545 | |||
| 2546 | // create shim container and insert it at an absolute position into the outer container |
||
| 2547 | shimContainer = document.createElement('div'); |
||
| 2548 | shimContainer.id = this.shimid; |
||
| 2549 | shimContainer.className = 'moxie-shim moxie-shim-' + this.type; |
||
| 2550 | |||
| 2551 | Basic.extend(shimContainer.style, { |
||
| 2552 | position: 'absolute', |
||
| 2553 | top: '0px', |
||
| 2554 | left: '0px', |
||
| 2555 | width: '1px', |
||
| 2556 | height: '1px', |
||
| 2557 | overflow: 'hidden' |
||
| 2558 | }); |
||
| 2559 | |||
| 2560 | container.appendChild(shimContainer); |
||
| 2561 | container = null; |
||
| 2562 | } |
||
| 2563 | |||
| 2564 | return shimContainer; |
||
| 2565 | }, |
||
| 2566 | |||
| 2567 | /** |
||
| 2568 | Returns runtime as DOM element (if appropriate) |
||
| 2569 | |||
| 2570 | @method getShim |
||
| 2571 | @return {DOMElement} |
||
| 2572 | */ |
||
| 2573 | getShim: function() { |
||
| 2574 | return _shim; |
||
| 2575 | }, |
||
| 2576 | |||
| 2577 | /** |
||
| 2578 | Invokes a method within the runtime itself (might differ across the runtimes) |
||
| 2579 | |||
| 2580 | @method shimExec |
||
| 2581 | @param {Mixed} [] |
||
| 2582 | @protected |
||
| 2583 | @return {Mixed} Depends on the action and component |
||
| 2584 | */ |
||
| 2585 | shimExec: function(component, action) { |
||
| 2586 | var args = [].slice.call(arguments, 2); |
||
| 2587 | return self.getShim().exec.call(this, this.uid, component, action, args); |
||
| 2588 | }, |
||
| 2589 | |||
| 2590 | /** |
||
| 2591 | Operaional interface that is used by components to invoke specific actions on the runtime |
||
| 2592 | (is invoked in the scope of component) |
||
| 2593 | |||
| 2594 | @method exec |
||
| 2595 | @param {Mixed} []* |
||
| 2596 | @protected |
||
| 2597 | @return {Mixed} Depends on the action and component |
||
| 2598 | */ |
||
| 2599 | exec: function(component, action) { // this is called in the context of component, not runtime |
||
| 2600 | var args = [].slice.call(arguments, 2); |
||
| 2601 | |||
| 2602 | if (self[component] && self[component][action]) { |
||
| 2603 | return self[component][action].apply(this, args); |
||
| 2604 | } |
||
| 2605 | return self.shimExec.apply(this, arguments); |
||
| 2606 | }, |
||
| 2607 | |||
| 2608 | /** |
||
| 2609 | Destroys the runtime (removes all events and deletes DOM structures) |
||
| 2610 | |||
| 2611 | @method destroy |
||
| 2612 | */ |
||
| 2613 | destroy: function() { |
||
| 2614 | if (!self) { |
||
| 2615 | return; // obviously already destroyed |
||
| 2616 | } |
||
| 2617 | |||
| 2618 | var shimContainer = Dom.get(this.shimid); |
||
| 2619 | if (shimContainer) { |
||
| 2620 | shimContainer.parentNode.removeChild(shimContainer); |
||
| 2621 | } |
||
| 2622 | |||
| 2623 | if (_shim) { |
||
| 2624 | _shim.removeAllInstances(); |
||
| 2625 | } |
||
| 2626 | |||
| 2627 | this.unbindAll(); |
||
| 2628 | delete runtimes[this.uid]; |
||
| 2629 | this.uid = null; // mark this runtime as destroyed |
||
| 2630 | _uid = self = _shim = shimContainer = null; |
||
| 2631 | } |
||
| 2632 | }); |
||
| 2633 | |||
| 2634 | // once we got the mode, test against all caps |
||
| 2635 | if (this.mode && options.required_caps && !this.can(options.required_caps)) { |
||
| 2636 | this.mode = false; |
||
| 2637 | } |
||
| 2638 | } |
||
| 2639 | |||
| 2640 | |||
| 2641 | /** |
||
| 2642 | Default order to try different runtime types |
||
| 2643 | |||
| 2644 | @property order |
||
| 2645 | @type String |
||
| 2646 | @static |
||
| 2647 | */ |
||
| 2648 | Runtime.order = 'html5,flash,silverlight,html4'; |
||
| 2649 | |||
| 2650 | |||
| 2651 | /** |
||
| 2652 | Retrieves runtime from private hash by it's uid |
||
| 2653 | |||
| 2654 | @method getRuntime |
||
| 2655 | @private |
||
| 2656 | @static |
||
| 2657 | @param {String} uid Unique identifier of the runtime |
||
| 2658 | @return {Runtime|Boolean} Returns runtime, if it exists and false, if - not |
||
| 2659 | */ |
||
| 2660 | Runtime.getRuntime = function(uid) { |
||
| 2661 | return runtimes[uid] ? runtimes[uid] : false; |
||
| 2662 | }; |
||
| 2663 | |||
| 2664 | |||
| 2665 | /** |
||
| 2666 | Register constructor for the Runtime of new (or perhaps modified) type |
||
| 2667 | |||
| 2668 | @method addConstructor |
||
| 2669 | @static |
||
| 2670 | @param {String} type Runtime type (e.g. flash, html5, etc) |
||
| 2671 | @param {Function} construct Constructor for the Runtime type |
||
| 2672 | */ |
||
| 2673 | Runtime.addConstructor = function(type, constructor) { |
||
| 2674 | constructor.prototype = EventTarget.instance; |
||
| 2675 | runtimeConstructors[type] = constructor; |
||
| 2676 | }; |
||
| 2677 | |||
| 2678 | |||
| 2679 | /** |
||
| 2680 | Get the constructor for the specified type. |
||
| 2681 | |||
| 2682 | method getConstructor |
||
| 2683 | @static |
||
| 2684 | @param {String} type Runtime type (e.g. flash, html5, etc) |
||
| 2685 | @return {Function} Constructor for the Runtime type |
||
| 2686 | */ |
||
| 2687 | Runtime.getConstructor = function(type) { |
||
| 2688 | return runtimeConstructors[type] || null; |
||
| 2689 | }; |
||
| 2690 | |||
| 2691 | |||
| 2692 | /** |
||
| 2693 | Get info about the runtime (uid, type, capabilities) |
||
| 2694 | |||
| 2695 | @method getInfo |
||
| 2696 | @static |
||
| 2697 | @param {String} uid Unique identifier of the runtime |
||
| 2698 | @return {Mixed} Info object or null if runtime doesn't exist |
||
| 2699 | */ |
||
| 2700 | Runtime.getInfo = function(uid) { |
||
| 2701 | var runtime = Runtime.getRuntime(uid); |
||
| 2702 | |||
| 2703 | if (runtime) { |
||
| 2704 | return { |
||
| 2705 | uid: runtime.uid, |
||
| 2706 | type: runtime.type, |
||
| 2707 | mode: runtime.mode, |
||
| 2708 | can: function() { |
||
| 2709 | return runtime.can.apply(runtime, arguments); |
||
| 2710 | } |
||
| 2711 | }; |
||
| 2712 | } |
||
| 2713 | return null; |
||
| 2714 | }; |
||
| 2715 | |||
| 2716 | |||
| 2717 | /** |
||
| 2718 | Convert caps represented by a comma-separated string to the object representation. |
||
| 2719 | |||
| 2720 | @method parseCaps |
||
| 2721 | @static |
||
| 2722 | @param {String} capStr Comma-separated list of capabilities |
||
| 2723 | @return {Object} |
||
| 2724 | */ |
||
| 2725 | Runtime.parseCaps = function(capStr) { |
||
| 2726 | var capObj = {}; |
||
| 2727 | |||
| 2728 | if (Basic.typeOf(capStr) !== 'string') { |
||
| 2729 | return capStr || {}; |
||
| 2730 | } |
||
| 2731 | |||
| 2732 | Basic.each(capStr.split(','), function(key) { |
||
| 2733 | capObj[key] = true; // we assume it to be - true |
||
| 2734 | }); |
||
| 2735 | |||
| 2736 | return capObj; |
||
| 2737 | }; |
||
| 2738 | |||
| 2739 | /** |
||
| 2740 | Test the specified runtime for specific capabilities. |
||
| 2741 | |||
| 2742 | @method can |
||
| 2743 | @static |
||
| 2744 | @param {String} type Runtime type (e.g. flash, html5, etc) |
||
| 2745 | @param {String|Object} caps Set of capabilities to check |
||
| 2746 | @return {Boolean} Result of the test |
||
| 2747 | */ |
||
| 2748 | Runtime.can = function(type, caps) { |
||
| 2749 | var runtime |
||
| 2750 | , constructor = Runtime.getConstructor(type) |
||
| 2751 | , mode |
||
| 2752 | ; |
||
| 2753 | if (constructor) { |
||
| 2754 | runtime = new constructor({ |
||
| 2755 | required_caps: caps |
||
| 2756 | }); |
||
| 2757 | mode = runtime.mode; |
||
| 2758 | runtime.destroy(); |
||
| 2759 | return !!mode; |
||
| 2760 | } |
||
| 2761 | return false; |
||
| 2762 | }; |
||
| 2763 | |||
| 2764 | |||
| 2765 | /** |
||
| 2766 | Figure out a runtime that supports specified capabilities. |
||
| 2767 | |||
| 2768 | @method thatCan |
||
| 2769 | @static |
||
| 2770 | @param {String|Object} caps Set of capabilities to check |
||
| 2771 | @param {String} [runtimeOrder] Comma-separated list of runtimes to check against |
||
| 2772 | @return {String} Usable runtime identifier or null |
||
| 2773 | */ |
||
| 2774 | Runtime.thatCan = function(caps, runtimeOrder) { |
||
| 2775 | var types = (runtimeOrder || Runtime.order).split(/\s*,\s*/); |
||
| 2776 | for (var i in types) { |
||
| 2777 | if (Runtime.can(types[i], caps)) { |
||
| 2778 | return types[i]; |
||
| 2779 | } |
||
| 2780 | } |
||
| 2781 | return null; |
||
| 2782 | }; |
||
| 2783 | |||
| 2784 | |||
| 2785 | /** |
||
| 2786 | Figure out an operational mode for the specified set of capabilities. |
||
| 2787 | |||
| 2788 | @method getMode |
||
| 2789 | @static |
||
| 2790 | @param {Object} modeCaps Set of capabilities that depend on particular runtime mode |
||
| 2791 | @param {Object} [requiredCaps] Supplied set of capabilities to find operational mode for |
||
| 2792 | @param {String|Boolean} [defaultMode='browser'] Default mode to use |
||
| 2793 | @return {String|Boolean} Compatible operational mode |
||
| 2794 | */ |
||
| 2795 | Runtime.getMode = function(modeCaps, requiredCaps, defaultMode) { |
||
| 2796 | var mode = null; |
||
| 2797 | |||
| 2798 | if (Basic.typeOf(defaultMode) === 'undefined') { // only if not specified |
||
| 2799 | defaultMode = 'browser'; |
||
| 2800 | } |
||
| 2801 | |||
| 2802 | if (requiredCaps && !Basic.isEmptyObj(modeCaps)) { |
||
| 2803 | // loop over required caps and check if they do require the same mode |
||
| 2804 | Basic.each(requiredCaps, function(value, cap) { |
||
| 2805 | if (modeCaps.hasOwnProperty(cap)) { |
||
| 2806 | var capMode = modeCaps[cap](value); |
||
| 2807 | |||
| 2808 | // make sure we always have an array |
||
| 2809 | if (typeof(capMode) === 'string') { |
||
| 2810 | capMode = [capMode]; |
||
| 2811 | } |
||
| 2812 | |||
| 2813 | if (!mode) { |
||
| 2814 | mode = capMode; |
||
| 2815 | } else if (!(mode = Basic.arrayIntersect(mode, capMode))) { |
||
| 2816 | // if cap requires conflicting mode - runtime cannot fulfill required caps |
||
| 2817 | |||
| 2818 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2819 | Env.log("\t\t%s: %s (conflicting mode requested: %s)", cap, value, capMode); |
||
| 2820 | } |
||
| 2821 | |||
| 2822 | return (mode = false); |
||
| 2823 | } |
||
| 2824 | } |
||
| 2825 | |||
| 2826 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2827 | Env.log("\t\t%s: %s (compatible modes: %s)", cap, value, mode); |
||
| 2828 | } |
||
| 2829 | }); |
||
| 2830 | |||
| 2831 | if (mode) { |
||
| 2832 | return Basic.inArray(defaultMode, mode) !== -1 ? defaultMode : mode[0]; |
||
| 2833 | } else if (mode === false) { |
||
| 2834 | return false; |
||
| 2835 | } |
||
| 2836 | } |
||
| 2837 | return defaultMode; |
||
| 2838 | }; |
||
| 2839 | |||
| 2840 | |||
| 2841 | /** |
||
| 2842 | * Third party shims (Flash and Silverlight) require global event target against which they |
||
| 2843 | * will fire their events. However when moxie is not loaded to global namespace, default |
||
| 2844 | * event target is not accessible and we have to create artificial ones. |
||
| 2845 | * |
||
| 2846 | * @method getGlobalEventTarget |
||
| 2847 | * @static |
||
| 2848 | * @return {String} Name of the global event target |
||
| 2849 | */ |
||
| 2850 | Runtime.getGlobalEventTarget = function() { |
||
| 2851 | if (/^moxie\./.test(Env.global_event_dispatcher) && !Env.can('access_global_ns')) { |
||
| 2852 | var uniqueCallbackName = Basic.guid('moxie_event_target_'); |
||
| 2853 | |||
| 2854 | window[uniqueCallbackName] = function(e, data) { |
||
| 2855 | EventTarget.instance.dispatchEvent(e, data); |
||
| 2856 | }; |
||
| 2857 | |||
| 2858 | Env.global_event_dispatcher = uniqueCallbackName; |
||
| 2859 | } |
||
| 2860 | |||
| 2861 | return Env.global_event_dispatcher; |
||
| 2862 | }; |
||
| 2863 | |||
| 2864 | |||
| 2865 | /** |
||
| 2866 | Capability check that always returns true |
||
| 2867 | |||
| 2868 | @private |
||
| 2869 | @static |
||
| 2870 | @return {True} |
||
| 2871 | */ |
||
| 2872 | Runtime.capTrue = function() { |
||
| 2873 | return true; |
||
| 2874 | }; |
||
| 2875 | |||
| 2876 | /** |
||
| 2877 | Capability check that always returns false |
||
| 2878 | |||
| 2879 | @private |
||
| 2880 | @static |
||
| 2881 | @return {False} |
||
| 2882 | */ |
||
| 2883 | Runtime.capFalse = function() { |
||
| 2884 | return false; |
||
| 2885 | }; |
||
| 2886 | |||
| 2887 | /** |
||
| 2888 | Evaluate the expression to boolean value and create a function that always returns it. |
||
| 2889 | |||
| 2890 | @private |
||
| 2891 | @static |
||
| 2892 | @param {Mixed} expr Expression to evaluate |
||
| 2893 | @return {Function} Function returning the result of evaluation |
||
| 2894 | */ |
||
| 2895 | Runtime.capTest = function(expr) { |
||
| 2896 | return function() { |
||
| 2897 | return !!expr; |
||
| 2898 | }; |
||
| 2899 | }; |
||
| 2900 | |||
| 2901 | return Runtime; |
||
| 2902 | }); |
||
| 2903 | |||
| 2904 | // Included from: src/javascript/runtime/RuntimeClient.js |
||
| 2905 | |||
| 2906 | /** |
||
| 2907 | * RuntimeClient.js |
||
| 2908 | * |
||
| 2909 | * Copyright 2013, Moxiecode Systems AB |
||
| 2910 | * Released under GPL License. |
||
| 2911 | * |
||
| 2912 | * License: http://www.plupload.com/license |
||
| 2913 | * Contributing: http://www.plupload.com/contributing |
||
| 2914 | */ |
||
| 2915 | |||
| 2916 | define('moxie/runtime/RuntimeClient', [ |
||
| 2917 | 'moxie/core/utils/Env', |
||
| 2918 | 'moxie/core/Exceptions', |
||
| 2919 | 'moxie/core/utils/Basic', |
||
| 2920 | 'moxie/runtime/Runtime' |
||
| 2921 | ], function(Env, x, Basic, Runtime) { |
||
| 2922 | /** |
||
| 2923 | Set of methods and properties, required by a component to acquire ability to connect to a runtime |
||
| 2924 | |||
| 2925 | @class moxie/runtime/RuntimeClient |
||
| 2926 | */ |
||
| 2927 | return function RuntimeClient() { |
||
| 2928 | var runtime; |
||
| 2929 | |||
| 2930 | Basic.extend(this, { |
||
| 2931 | /** |
||
| 2932 | Connects to the runtime specified by the options. Will either connect to existing runtime or create a new one. |
||
| 2933 | Increments number of clients connected to the specified runtime. |
||
| 2934 | |||
| 2935 | @private |
||
| 2936 | @method connectRuntime |
||
| 2937 | @param {Mixed} options Can be a runtme uid or a set of key-value pairs defining requirements and pre-requisites |
||
| 2938 | */ |
||
| 2939 | connectRuntime: function(options) { |
||
| 2940 | var comp = this, ruid; |
||
| 2941 | |||
| 2942 | function initialize(items) { |
||
| 2943 | var type, constructor; |
||
| 2944 | |||
| 2945 | // if we ran out of runtimes |
||
| 2946 | if (!items.length) { |
||
| 2947 | comp.trigger('RuntimeError', new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR)); |
||
| 2948 | runtime = null; |
||
| 2949 | return; |
||
| 2950 | } |
||
| 2951 | |||
| 2952 | type = items.shift().toLowerCase(); |
||
| 2953 | constructor = Runtime.getConstructor(type); |
||
| 2954 | if (!constructor) { |
||
| 2955 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2956 | Env.log("Constructor for '%s' runtime is not available.", type); |
||
| 2957 | } |
||
| 2958 | initialize(items); |
||
| 2959 | return; |
||
| 2960 | } |
||
| 2961 | |||
| 2962 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2963 | Env.log("Trying runtime: %s", type); |
||
| 2964 | Env.log(options); |
||
| 2965 | } |
||
| 2966 | |||
| 2967 | // try initializing the runtime |
||
| 2968 | runtime = new constructor(options); |
||
| 2969 | |||
| 2970 | runtime.bind('Init', function() { |
||
| 2971 | // mark runtime as initialized |
||
| 2972 | runtime.initialized = true; |
||
| 2973 | |||
| 2974 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2975 | Env.log("Runtime '%s' initialized", runtime.type); |
||
| 2976 | } |
||
| 2977 | |||
| 2978 | // jailbreak ... |
||
| 2979 | setTimeout(function() { |
||
| 2980 | runtime.clients++; |
||
| 2981 | comp.ruid = runtime.uid; |
||
| 2982 | // this will be triggered on component |
||
| 2983 | comp.trigger('RuntimeInit', runtime); |
||
| 2984 | }, 1); |
||
| 2985 | }); |
||
| 2986 | |||
| 2987 | runtime.bind('Error', function() { |
||
| 2988 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 2989 | Env.log("Runtime '%s' failed to initialize", runtime.type); |
||
| 2990 | } |
||
| 2991 | |||
| 2992 | runtime.destroy(); // runtime cannot destroy itself from inside at a right moment, thus we do it here |
||
| 2993 | initialize(items); |
||
| 2994 | }); |
||
| 2995 | |||
| 2996 | runtime.bind('Exception', function(e, err) { |
||
| 2997 | var message = err.name + "(#" + err.code + ")" + (err.message ? ", from: " + err.message : ''); |
||
| 2998 | |||
| 2999 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 3000 | Env.log("Runtime '%s' has thrown an exception: %s", this.type, message); |
||
| 3001 | } |
||
| 3002 | comp.trigger('RuntimeError', new x.RuntimeError(x.RuntimeError.EXCEPTION_ERR, message)); |
||
| 3003 | }); |
||
| 3004 | |||
| 3005 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 3006 | Env.log("\tselected mode: %s", runtime.mode); |
||
| 3007 | } |
||
| 3008 | |||
| 3009 | // check if runtime managed to pick-up operational mode |
||
| 3010 | if (!runtime.mode) { |
||
| 3011 | runtime.trigger('Error'); |
||
| 3012 | return; |
||
| 3013 | } |
||
| 3014 | |||
| 3015 | runtime.init(); |
||
| 3016 | } |
||
| 3017 | |||
| 3018 | // check if a particular runtime was requested |
||
| 3019 | if (Basic.typeOf(options) === 'string') { |
||
| 3020 | ruid = options; |
||
| 3021 | } else if (Basic.typeOf(options.ruid) === 'string') { |
||
| 3022 | ruid = options.ruid; |
||
| 3023 | } |
||
| 3024 | |||
| 3025 | if (ruid) { |
||
| 3026 | runtime = Runtime.getRuntime(ruid); |
||
| 3027 | if (runtime) { |
||
| 3028 | comp.ruid = ruid; |
||
| 3029 | runtime.clients++; |
||
| 3030 | return runtime; |
||
| 3031 | } else { |
||
| 3032 | // there should be a runtime and there's none - weird case |
||
| 3033 | throw new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR); |
||
| 3034 | } |
||
| 3035 | } |
||
| 3036 | |||
| 3037 | // initialize a fresh one, that fits runtime list and required features best |
||
| 3038 | initialize((options.runtime_order || Runtime.order).split(/\s*,\s*/)); |
||
| 3039 | }, |
||
| 3040 | |||
| 3041 | |||
| 3042 | /** |
||
| 3043 | Disconnects from the runtime. Decrements number of clients connected to the specified runtime. |
||
| 3044 | |||
| 3045 | @private |
||
| 3046 | @method disconnectRuntime |
||
| 3047 | */ |
||
| 3048 | disconnectRuntime: function() { |
||
| 3049 | if (runtime && --runtime.clients <= 0) { |
||
| 3050 | runtime.destroy(); |
||
| 3051 | } |
||
| 3052 | |||
| 3053 | // once the component is disconnected, it shouldn't have access to the runtime |
||
| 3054 | runtime = null; |
||
| 3055 | }, |
||
| 3056 | |||
| 3057 | |||
| 3058 | /** |
||
| 3059 | Returns the runtime to which the client is currently connected. |
||
| 3060 | |||
| 3061 | @method getRuntime |
||
| 3062 | @return {Runtime} Runtime or null if client is not connected |
||
| 3063 | */ |
||
| 3064 | getRuntime: function() { |
||
| 3065 | if (runtime && runtime.uid) { |
||
| 3066 | return runtime; |
||
| 3067 | } |
||
| 3068 | return runtime = null; // make sure we do not leave zombies rambling around |
||
| 3069 | }, |
||
| 3070 | |||
| 3071 | |||
| 3072 | /** |
||
| 3073 | Handy shortcut to safely invoke runtime extension methods. |
||
| 3074 | |||
| 3075 | @private |
||
| 3076 | @method exec |
||
| 3077 | @return {Mixed} Whatever runtime extension method returns |
||
| 3078 | */ |
||
| 3079 | exec: function() { |
||
| 3080 | return runtime ? runtime.exec.apply(this, arguments) : null; |
||
| 3081 | }, |
||
| 3082 | |||
| 3083 | |||
| 3084 | /** |
||
| 3085 | Test runtime client for specific capability |
||
| 3086 | |||
| 3087 | @method can |
||
| 3088 | @param {String} cap |
||
| 3089 | @return {Bool} |
||
| 3090 | */ |
||
| 3091 | can: function(cap) { |
||
| 3092 | return runtime ? runtime.can(cap) : false; |
||
| 3093 | } |
||
| 3094 | |||
| 3095 | }); |
||
| 3096 | }; |
||
| 3097 | |||
| 3098 | |||
| 3099 | }); |
||
| 3100 | |||
| 3101 | // Included from: src/javascript/file/Blob.js |
||
| 3102 | |||
| 3103 | /** |
||
| 3104 | * Blob.js |
||
| 3105 | * |
||
| 3106 | * Copyright 2013, Moxiecode Systems AB |
||
| 3107 | * Released under GPL License. |
||
| 3108 | * |
||
| 3109 | * License: http://www.plupload.com/license |
||
| 3110 | * Contributing: http://www.plupload.com/contributing |
||
| 3111 | */ |
||
| 3112 | |||
| 3113 | define('moxie/file/Blob', [ |
||
| 3114 | 'moxie/core/utils/Basic', |
||
| 3115 | 'moxie/core/utils/Encode', |
||
| 3116 | 'moxie/runtime/RuntimeClient' |
||
| 3117 | ], function(Basic, Encode, RuntimeClient) { |
||
| 3118 | |||
| 3119 | var blobpool = {}; |
||
| 3120 | |||
| 3121 | /** |
||
| 3122 | @class moxie/file/Blob |
||
| 3123 | @constructor |
||
| 3124 | @param {String} ruid Unique id of the runtime, to which this blob belongs to |
||
| 3125 | @param {Object} blob Object "Native" blob object, as it is represented in the runtime |
||
| 3126 | */ |
||
| 3127 | function Blob(ruid, blob) { |
||
| 3128 | |||
| 3129 | function _sliceDetached(start, end, type) { |
||
| 3130 | var blob, data = blobpool[this.uid]; |
||
| 3131 | |||
| 3132 | if (Basic.typeOf(data) !== 'string' || !data.length) { |
||
| 3133 | return null; // or throw exception |
||
| 3134 | } |
||
| 3135 | |||
| 3136 | blob = new Blob(null, { |
||
| 3137 | type: type, |
||
| 3138 | size: end - start |
||
| 3139 | }); |
||
| 3140 | blob.detach(data.substr(start, blob.size)); |
||
| 3141 | |||
| 3142 | return blob; |
||
| 3143 | } |
||
| 3144 | |||
| 3145 | RuntimeClient.call(this); |
||
| 3146 | |||
| 3147 | if (ruid) { |
||
| 3148 | this.connectRuntime(ruid); |
||
| 3149 | } |
||
| 3150 | |||
| 3151 | if (!blob) { |
||
| 3152 | blob = {}; |
||
| 3153 | } else if (Basic.typeOf(blob) === 'string') { // dataUrl or binary string |
||
| 3154 | blob = { data: blob }; |
||
| 3155 | } |
||
| 3156 | |||
| 3157 | Basic.extend(this, { |
||
| 3158 | |||
| 3159 | /** |
||
| 3160 | Unique id of the component |
||
| 3161 | |||
| 3162 | @property uid |
||
| 3163 | @type {String} |
||
| 3164 | */ |
||
| 3165 | uid: blob.uid || Basic.guid('uid_'), |
||
| 3166 | |||
| 3167 | /** |
||
| 3168 | Unique id of the connected runtime, if falsy, then runtime will have to be initialized |
||
| 3169 | before this Blob can be used, modified or sent |
||
| 3170 | |||
| 3171 | @property ruid |
||
| 3172 | @type {String} |
||
| 3173 | */ |
||
| 3174 | ruid: ruid, |
||
| 3175 | |||
| 3176 | /** |
||
| 3177 | Size of blob |
||
| 3178 | |||
| 3179 | @property size |
||
| 3180 | @type {Number} |
||
| 3181 | @default 0 |
||
| 3182 | */ |
||
| 3183 | size: blob.size || 0, |
||
| 3184 | |||
| 3185 | /** |
||
| 3186 | Mime type of blob |
||
| 3187 | |||
| 3188 | @property type |
||
| 3189 | @type {String} |
||
| 3190 | @default '' |
||
| 3191 | */ |
||
| 3192 | type: blob.type || '', |
||
| 3193 | |||
| 3194 | /** |
||
| 3195 | @method slice |
||
| 3196 | @param {Number} [start=0] |
||
| 3197 | */ |
||
| 3198 | slice: function(start, end, type) { |
||
| 3199 | if (this.isDetached()) { |
||
| 3200 | return _sliceDetached.apply(this, arguments); |
||
| 3201 | } |
||
| 3202 | return this.getRuntime().exec.call(this, 'Blob', 'slice', this.getSource(), start, end, type); |
||
| 3203 | }, |
||
| 3204 | |||
| 3205 | /** |
||
| 3206 | Returns "native" blob object (as it is represented in connected runtime) or null if not found |
||
| 3207 | |||
| 3208 | @method getSource |
||
| 3209 | @return {Blob} Returns "native" blob object or null if not found |
||
| 3210 | */ |
||
| 3211 | getSource: function() { |
||
| 3212 | if (!blobpool[this.uid]) { |
||
| 3213 | return null; |
||
| 3214 | } |
||
| 3215 | return blobpool[this.uid]; |
||
| 3216 | }, |
||
| 3217 | |||
| 3218 | /** |
||
| 3219 | Detaches blob from any runtime that it depends on and initialize with standalone value |
||
| 3220 | |||
| 3221 | @method detach |
||
| 3222 | @protected |
||
| 3223 | @param {DOMString} [data=''] Standalone value |
||
| 3224 | */ |
||
| 3225 | detach: function(data) { |
||
| 3226 | if (this.ruid) { |
||
| 3227 | this.getRuntime().exec.call(this, 'Blob', 'destroy'); |
||
| 3228 | this.disconnectRuntime(); |
||
| 3229 | this.ruid = null; |
||
| 3230 | } |
||
| 3231 | |||
| 3232 | data = data || ''; |
||
| 3233 | |||
| 3234 | // if dataUrl, convert to binary string |
||
| 3235 | if (data.substr(0, 5) == 'data:') { |
||
| 3236 | var base64Offset = data.indexOf(';base64,'); |
||
| 3237 | this.type = data.substring(5, base64Offset); |
||
| 3238 | data = Encode.atob(data.substring(base64Offset + 8)); |
||
| 3239 | } |
||
| 3240 | |||
| 3241 | this.size = data.length; |
||
| 3242 | |||
| 3243 | blobpool[this.uid] = data; |
||
| 3244 | }, |
||
| 3245 | |||
| 3246 | /** |
||
| 3247 | Checks if blob is standalone (detached of any runtime) |
||
| 3248 | |||
| 3249 | @method isDetached |
||
| 3250 | @protected |
||
| 3251 | @return {Boolean} |
||
| 3252 | */ |
||
| 3253 | isDetached: function() { |
||
| 3254 | return !this.ruid && Basic.typeOf(blobpool[this.uid]) === 'string'; |
||
| 3255 | }, |
||
| 3256 | |||
| 3257 | /** |
||
| 3258 | Destroy Blob and free any resources it was using |
||
| 3259 | |||
| 3260 | @method destroy |
||
| 3261 | */ |
||
| 3262 | destroy: function() { |
||
| 3263 | this.detach(); |
||
| 3264 | delete blobpool[this.uid]; |
||
| 3265 | } |
||
| 3266 | }); |
||
| 3267 | |||
| 3268 | |||
| 3269 | if (blob.data) { |
||
| 3270 | this.detach(blob.data); // auto-detach if payload has been passed |
||
| 3271 | } else { |
||
| 3272 | blobpool[this.uid] = blob; |
||
| 3273 | } |
||
| 3274 | } |
||
| 3275 | |||
| 3276 | return Blob; |
||
| 3277 | }); |
||
| 3278 | |||
| 3279 | // Included from: src/javascript/core/I18n.js |
||
| 3280 | |||
| 3281 | /** |
||
| 3282 | * I18n.js |
||
| 3283 | * |
||
| 3284 | * Copyright 2013, Moxiecode Systems AB |
||
| 3285 | * Released under GPL License. |
||
| 3286 | * |
||
| 3287 | * License: http://www.plupload.com/license |
||
| 3288 | * Contributing: http://www.plupload.com/contributing |
||
| 3289 | */ |
||
| 3290 | |||
| 3291 | define("moxie/core/I18n", [ |
||
| 3292 | "moxie/core/utils/Basic" |
||
| 3293 | ], function(Basic) { |
||
| 3294 | var i18n = {}; |
||
| 3295 | |||
| 3296 | /** |
||
| 3297 | @class moxie/core/I18n |
||
| 3298 | */ |
||
| 3299 | return { |
||
| 3300 | /** |
||
| 3301 | * Extends the language pack object with new items. |
||
| 3302 | * |
||
| 3303 | * @param {Object} pack Language pack items to add. |
||
| 3304 | * @return {Object} Extended language pack object. |
||
| 3305 | */ |
||
| 3306 | addI18n: function(pack) { |
||
| 3307 | return Basic.extend(i18n, pack); |
||
| 3308 | }, |
||
| 3309 | |||
| 3310 | /** |
||
| 3311 | * Translates the specified string by checking for the english string in the language pack lookup. |
||
| 3312 | * |
||
| 3313 | * @param {String} str String to look for. |
||
| 3314 | * @return {String} Translated string or the input string if it wasn't found. |
||
| 3315 | */ |
||
| 3316 | translate: function(str) { |
||
| 3317 | return i18n[str] || str; |
||
| 3318 | }, |
||
| 3319 | |||
| 3320 | /** |
||
| 3321 | * Shortcut for translate function |
||
| 3322 | * |
||
| 3323 | * @param {String} str String to look for. |
||
| 3324 | * @return {String} Translated string or the input string if it wasn't found. |
||
| 3325 | */ |
||
| 3326 | _: function(str) { |
||
| 3327 | return this.translate(str); |
||
| 3328 | }, |
||
| 3329 | |||
| 3330 | /** |
||
| 3331 | * Pseudo sprintf implementation - simple way to replace tokens with specified values. |
||
| 3332 | * |
||
| 3333 | * @param {String} str String with tokens |
||
| 3334 | * @return {String} String with replaced tokens |
||
| 3335 | */ |
||
| 3336 | sprintf: function(str) { |
||
| 3337 | var args = [].slice.call(arguments, 1); |
||
| 3338 | |||
| 3339 | return str.replace(/%[a-z]/g, function() { |
||
| 3340 | var value = args.shift(); |
||
| 3341 | return Basic.typeOf(value) !== 'undefined' ? value : ''; |
||
| 3342 | }); |
||
| 3343 | } |
||
| 3344 | }; |
||
| 3345 | }); |
||
| 3346 | |||
| 3347 | // Included from: src/javascript/core/utils/Mime.js |
||
| 3348 | |||
| 3349 | /** |
||
| 3350 | * Mime.js |
||
| 3351 | * |
||
| 3352 | * Copyright 2013, Moxiecode Systems AB |
||
| 3353 | * Released under GPL License. |
||
| 3354 | * |
||
| 3355 | * License: http://www.plupload.com/license |
||
| 3356 | * Contributing: http://www.plupload.com/contributing |
||
| 3357 | */ |
||
| 3358 | |||
| 3359 | /** |
||
| 3360 | @class moxie/core/utils/Mime |
||
| 3361 | @public |
||
| 3362 | @static |
||
| 3363 | */ |
||
| 3364 | |||
| 3365 | define("moxie/core/utils/Mime", [ |
||
| 3366 | "moxie/core/utils/Basic", |
||
| 3367 | "moxie/core/I18n" |
||
| 3368 | ], function(Basic, I18n) { |
||
| 3369 | |||
| 3370 | var mimeData = "" + |
||
| 3371 | "application/msword,doc dot," + |
||
| 3372 | "application/pdf,pdf," + |
||
| 3373 | "application/pgp-signature,pgp," + |
||
| 3374 | "application/postscript,ps ai eps," + |
||
| 3375 | "application/rtf,rtf," + |
||
| 3376 | "application/vnd.ms-excel,xls xlb xlt xla," + |
||
| 3377 | "application/vnd.ms-powerpoint,ppt pps pot ppa," + |
||
| 3378 | "application/zip,zip," + |
||
| 3379 | "application/x-shockwave-flash,swf swfl," + |
||
| 3380 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx," + |
||
| 3381 | "application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx," + |
||
| 3382 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx," + |
||
| 3383 | "application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx," + |
||
| 3384 | "application/vnd.openxmlformats-officedocument.presentationml.template,potx," + |
||
| 3385 | "application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx," + |
||
| 3386 | "application/x-javascript,js," + |
||
| 3387 | "application/json,json," + |
||
| 3388 | "audio/mpeg,mp3 mpga mpega mp2," + |
||
| 3389 | "audio/x-wav,wav," + |
||
| 3390 | "audio/x-m4a,m4a," + |
||
| 3391 | "audio/ogg,oga ogg," + |
||
| 3392 | "audio/aiff,aiff aif," + |
||
| 3393 | "audio/flac,flac," + |
||
| 3394 | "audio/aac,aac," + |
||
| 3395 | "audio/ac3,ac3," + |
||
| 3396 | "audio/x-ms-wma,wma," + |
||
| 3397 | "image/bmp,bmp," + |
||
| 3398 | "image/gif,gif," + |
||
| 3399 | "image/jpeg,jpg jpeg jpe," + |
||
| 3400 | "image/photoshop,psd," + |
||
| 3401 | "image/png,png," + |
||
| 3402 | "image/svg+xml,svg svgz," + |
||
| 3403 | "image/tiff,tiff tif," + |
||
| 3404 | "text/plain,asc txt text diff log," + |
||
| 3405 | "text/html,htm html xhtml," + |
||
| 3406 | "text/css,css," + |
||
| 3407 | "text/csv,csv," + |
||
| 3408 | "text/rtf,rtf," + |
||
| 3409 | "video/mpeg,mpeg mpg mpe m2v," + |
||
| 3410 | "video/quicktime,qt mov," + |
||
| 3411 | "video/mp4,mp4," + |
||
| 3412 | "video/x-m4v,m4v," + |
||
| 3413 | "video/x-flv,flv," + |
||
| 3414 | "video/x-ms-wmv,wmv," + |
||
| 3415 | "video/avi,avi," + |
||
| 3416 | "video/webm,webm," + |
||
| 3417 | "video/3gpp,3gpp 3gp," + |
||
| 3418 | "video/3gpp2,3g2," + |
||
| 3419 | "video/vnd.rn-realvideo,rv," + |
||
| 3420 | "video/ogg,ogv," + |
||
| 3421 | "video/x-matroska,mkv," + |
||
| 3422 | "application/vnd.oasis.opendocument.formula-template,otf," + |
||
| 3423 | "application/octet-stream,exe"; |
||
| 3424 | |||
| 3425 | |||
| 3426 | /** |
||
| 3427 | * Map of mimes to extensions |
||
| 3428 | * |
||
| 3429 | * @property mimes |
||
| 3430 | * @type {Object} |
||
| 3431 | */ |
||
| 3432 | var mimes = {}; |
||
| 3433 | |||
| 3434 | /** |
||
| 3435 | * Map of extensions to mimes |
||
| 3436 | * |
||
| 3437 | * @property extensions |
||
| 3438 | * @type {Object} |
||
| 3439 | */ |
||
| 3440 | var extensions = {}; |
||
| 3441 | |||
| 3442 | |||
| 3443 | /** |
||
| 3444 | * Parses mimeData string into a mimes and extensions lookup maps. String should have the |
||
| 3445 | * following format: |
||
| 3446 | * |
||
| 3447 | * application/msword,doc dot,application/pdf,pdf, ... |
||
| 3448 | * |
||
| 3449 | * so mime-type followed by comma and followed by space-separated list of associated extensions, |
||
| 3450 | * then comma again and then another mime-type, etc. |
||
| 3451 | * |
||
| 3452 | * If invoked externally will replace override internal lookup maps with user-provided data. |
||
| 3453 | * |
||
| 3454 | * @method addMimeType |
||
| 3455 | * @param {String} mimeData |
||
| 3456 | */ |
||
| 3457 | var addMimeType = function (mimeData) { |
||
| 3458 | var items = mimeData.split(/,/), i, ii, ext; |
||
| 3459 | |||
| 3460 | for (i = 0; i < items.length; i += 2) { |
||
| 3461 | ext = items[i + 1].split(/ /); |
||
| 3462 | |||
| 3463 | // extension to mime lookup |
||
| 3464 | for (ii = 0; ii < ext.length; ii++) { |
||
| 3465 | mimes[ext[ii]] = items[i]; |
||
| 3466 | } |
||
| 3467 | // mime to extension lookup |
||
| 3468 | extensions[items[i]] = ext; |
||
| 3469 | } |
||
| 3470 | }; |
||
| 3471 | |||
| 3472 | |||
| 3473 | var extList2mimes = function (filters, addMissingExtensions) { |
||
| 3474 | var ext, i, ii, type, mimes = []; |
||
| 3475 | |||
| 3476 | // convert extensions to mime types list |
||
| 3477 | for (i = 0; i < filters.length; i++) { |
||
| 3478 | ext = filters[i].extensions.toLowerCase().split(/\s*,\s*/); |
||
| 3479 | |||
| 3480 | for (ii = 0; ii < ext.length; ii++) { |
||
| 3481 | |||
| 3482 | // if there's an asterisk in the list, then accept attribute is not required |
||
| 3483 | if (ext[ii] === '*') { |
||
| 3484 | return []; |
||
| 3485 | } |
||
| 3486 | |||
| 3487 | type = mimes[ext[ii]]; |
||
| 3488 | |||
| 3489 | // future browsers should filter by extension, finally |
||
| 3490 | if (addMissingExtensions && /^\w+$/.test(ext[ii])) { |
||
| 3491 | mimes.push('.' + ext[ii]); |
||
| 3492 | } else if (type && Basic.inArray(type, mimes) === -1) { |
||
| 3493 | mimes.push(type); |
||
| 3494 | } else if (!type) { |
||
| 3495 | // if we have no type in our map, then accept all |
||
| 3496 | return []; |
||
| 3497 | } |
||
| 3498 | } |
||
| 3499 | } |
||
| 3500 | return mimes; |
||
| 3501 | }; |
||
| 3502 | |||
| 3503 | |||
| 3504 | var mimes2exts = function(mimes) { |
||
| 3505 | var exts = []; |
||
| 3506 | |||
| 3507 | Basic.each(mimes, function(mime) { |
||
| 3508 | mime = mime.toLowerCase(); |
||
| 3509 | |||
| 3510 | if (mime === '*') { |
||
| 3511 | exts = []; |
||
| 3512 | return false; |
||
| 3513 | } |
||
| 3514 | |||
| 3515 | // check if this thing looks like mime type |
||
| 3516 | var m = mime.match(/^(\w+)\/(\*|\w+)$/); |
||
| 3517 | if (m) { |
||
| 3518 | if (m[2] === '*') { |
||
| 3519 | // wildcard mime type detected |
||
| 3520 | Basic.each(extensions, function(arr, mime) { |
||
| 3521 | if ((new RegExp('^' + m[1] + '/')).test(mime)) { |
||
| 3522 | [].push.apply(exts, extensions[mime]); |
||
| 3523 | } |
||
| 3524 | }); |
||
| 3525 | } else if (extensions[mime]) { |
||
| 3526 | [].push.apply(exts, extensions[mime]); |
||
| 3527 | } |
||
| 3528 | } |
||
| 3529 | }); |
||
| 3530 | return exts; |
||
| 3531 | }; |
||
| 3532 | |||
| 3533 | |||
| 3534 | var mimes2extList = function(mimes) { |
||
| 3535 | var accept = [], exts = []; |
||
| 3536 | |||
| 3537 | if (Basic.typeOf(mimes) === 'string') { |
||
| 3538 | mimes = Basic.trim(mimes).split(/\s*,\s*/); |
||
| 3539 | } |
||
| 3540 | |||
| 3541 | exts = mimes2exts(mimes); |
||
| 3542 | |||
| 3543 | accept.push({ |
||
| 3544 | title: I18n.translate('Files'), |
||
| 3545 | extensions: exts.length ? exts.join(',') : '*' |
||
| 3546 | }); |
||
| 3547 | |||
| 3548 | return accept; |
||
| 3549 | }; |
||
| 3550 | |||
| 3551 | /** |
||
| 3552 | * Extract extension from the given filename |
||
| 3553 | * |
||
| 3554 | * @method getFileExtension |
||
| 3555 | * @param {String} fileName |
||
| 3556 | * @return {String} File extension |
||
| 3557 | */ |
||
| 3558 | var getFileExtension = function(fileName) { |
||
| 3559 | var matches = fileName && fileName.match(/\.([^.]+)$/); |
||
| 3560 | if (matches) { |
||
| 3561 | return matches[1].toLowerCase(); |
||
| 3562 | } |
||
| 3563 | return ''; |
||
| 3564 | }; |
||
| 3565 | |||
| 3566 | |||
| 3567 | /** |
||
| 3568 | * Get file mime-type from it's filename - will try to match the extension |
||
| 3569 | * against internal mime-type lookup map |
||
| 3570 | * |
||
| 3571 | * @method getFileMime |
||
| 3572 | * @param {String} fileName |
||
| 3573 | * @return File mime-type if found or an empty string if not |
||
| 3574 | */ |
||
| 3575 | var getFileMime = function(fileName) { |
||
| 3576 | return mimes[getFileExtension(fileName)] || ''; |
||
| 3577 | }; |
||
| 3578 | |||
| 3579 | |||
| 3580 | addMimeType(mimeData); |
||
| 3581 | |||
| 3582 | return { |
||
| 3583 | mimes: mimes, |
||
| 3584 | extensions: extensions, |
||
| 3585 | addMimeType: addMimeType, |
||
| 3586 | extList2mimes: extList2mimes, |
||
| 3587 | mimes2exts: mimes2exts, |
||
| 3588 | mimes2extList: mimes2extList, |
||
| 3589 | getFileExtension: getFileExtension, |
||
| 3590 | getFileMime: getFileMime |
||
| 3591 | } |
||
| 3592 | }); |
||
| 3593 | |||
| 3594 | // Included from: src/javascript/file/FileInput.js |
||
| 3595 | |||
| 3596 | /** |
||
| 3597 | * FileInput.js |
||
| 3598 | * |
||
| 3599 | * Copyright 2013, Moxiecode Systems AB |
||
| 3600 | * Released under GPL License. |
||
| 3601 | * |
||
| 3602 | * License: http://www.plupload.com/license |
||
| 3603 | * Contributing: http://www.plupload.com/contributing |
||
| 3604 | */ |
||
| 3605 | |||
| 3606 | define('moxie/file/FileInput', [ |
||
| 3607 | 'moxie/core/utils/Basic', |
||
| 3608 | 'moxie/core/utils/Env', |
||
| 3609 | 'moxie/core/utils/Mime', |
||
| 3610 | 'moxie/core/utils/Dom', |
||
| 3611 | 'moxie/core/Exceptions', |
||
| 3612 | 'moxie/core/EventTarget', |
||
| 3613 | 'moxie/core/I18n', |
||
| 3614 | 'moxie/runtime/Runtime', |
||
| 3615 | 'moxie/runtime/RuntimeClient' |
||
| 3616 | ], function(Basic, Env, Mime, Dom, x, EventTarget, I18n, Runtime, RuntimeClient) { |
||
| 3617 | /** |
||
| 3618 | Provides a convenient way to create cross-browser file-picker. Generates file selection dialog on click, |
||
| 3619 | converts selected files to _File_ objects, to be used in conjunction with _Image_, preloaded in memory |
||
| 3620 | with _FileReader_ or uploaded to a server through _XMLHttpRequest_. |
||
| 3621 | |||
| 3622 | @class moxie/file/FileInput |
||
| 3623 | @constructor |
||
| 3624 | @extends EventTarget |
||
| 3625 | @uses RuntimeClient |
||
| 3626 | @param {Object|String|DOMElement} options If options is string or node, argument is considered as _browse\_button_. |
||
| 3627 | @param {String|DOMElement} options.browse_button DOM Element to turn into file picker. |
||
| 3628 | @param {Array} [options.accept] Array of mime types to accept. By default accepts all. |
||
| 3629 | @param {Boolean} [options.multiple=false] Enable selection of multiple files. |
||
| 3630 | @param {Boolean} [options.directory=false] Turn file input into the folder input (cannot be both at the same time). |
||
| 3631 | @param {String|DOMElement} [options.container] DOM Element to use as a container for file-picker. Defaults to parentNode |
||
| 3632 | for _browse\_button_. |
||
| 3633 | @param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support. |
||
| 3634 | |||
| 3635 | @example |
||
| 3636 | <div id="container"> |
||
| 3637 | <a id="file-picker" href="javascript:;">Browse...</a> |
||
| 3638 | </div> |
||
| 3639 | |||
| 3640 | <script> |
||
| 3641 | var fileInput = new moxie.file.FileInput({ |
||
| 3642 | browse_button: 'file-picker', // or document.getElementById('file-picker') |
||
| 3643 | container: 'container', |
||
| 3644 | accept: [ |
||
| 3645 | {title: "Image files", extensions: "jpg,gif,png"} // accept only images |
||
| 3646 | ], |
||
| 3647 | multiple: true // allow multiple file selection |
||
| 3648 | }); |
||
| 3649 | |||
| 3650 | fileInput.onchange = function(e) { |
||
| 3651 | // do something to files array |
||
| 3652 | console.info(e.target.files); // or this.files or fileInput.files |
||
| 3653 | }; |
||
| 3654 | |||
| 3655 | fileInput.init(); // initialize |
||
| 3656 | </script> |
||
| 3657 | */ |
||
| 3658 | var dispatches = [ |
||
| 3659 | /** |
||
| 3660 | Dispatched when runtime is connected and file-picker is ready to be used. |
||
| 3661 | |||
| 3662 | @event ready |
||
| 3663 | @param {Object} event |
||
| 3664 | */ |
||
| 3665 | 'ready', |
||
| 3666 | |||
| 3667 | /** |
||
| 3668 | Dispatched right after [ready](#event_ready) event, and whenever [refresh()](#method_refresh) is invoked. |
||
| 3669 | Check [corresponding documentation entry](#method_refresh) for more info. |
||
| 3670 | |||
| 3671 | @event refresh |
||
| 3672 | @param {Object} event |
||
| 3673 | */ |
||
| 3674 | |||
| 3675 | /** |
||
| 3676 | Dispatched when selection of files in the dialog is complete. |
||
| 3677 | |||
| 3678 | @event change |
||
| 3679 | @param {Object} event |
||
| 3680 | */ |
||
| 3681 | 'change', |
||
| 3682 | |||
| 3683 | 'cancel', // TODO: might be useful |
||
| 3684 | |||
| 3685 | /** |
||
| 3686 | Dispatched when mouse cursor enters file-picker area. Can be used to style element |
||
| 3687 | accordingly. |
||
| 3688 | |||
| 3689 | @event mouseenter |
||
| 3690 | @param {Object} event |
||
| 3691 | */ |
||
| 3692 | 'mouseenter', |
||
| 3693 | |||
| 3694 | /** |
||
| 3695 | Dispatched when mouse cursor leaves file-picker area. Can be used to style element |
||
| 3696 | accordingly. |
||
| 3697 | |||
| 3698 | @event mouseleave |
||
| 3699 | @param {Object} event |
||
| 3700 | */ |
||
| 3701 | 'mouseleave', |
||
| 3702 | |||
| 3703 | /** |
||
| 3704 | Dispatched when functional mouse button is pressed on top of file-picker area. |
||
| 3705 | |||
| 3706 | @event mousedown |
||
| 3707 | @param {Object} event |
||
| 3708 | */ |
||
| 3709 | 'mousedown', |
||
| 3710 | |||
| 3711 | /** |
||
| 3712 | Dispatched when functional mouse button is released on top of file-picker area. |
||
| 3713 | |||
| 3714 | @event mouseup |
||
| 3715 | @param {Object} event |
||
| 3716 | */ |
||
| 3717 | 'mouseup' |
||
| 3718 | ]; |
||
| 3719 | |||
| 3720 | function FileInput(options) { |
||
| 3721 | if (MXI_DEBUG) { |
||
| 3722 | Env.log("Instantiating FileInput..."); |
||
| 3723 | } |
||
| 3724 | |||
| 3725 | var container, browseButton, defaults; |
||
| 3726 | |||
| 3727 | // if flat argument passed it should be browse_button id |
||
| 3728 | if (Basic.inArray(Basic.typeOf(options), ['string', 'node']) !== -1) { |
||
| 3729 | options = { browse_button : options }; |
||
| 3730 | } |
||
| 3731 | |||
| 3732 | // this will help us to find proper default container |
||
| 3733 | browseButton = Dom.get(options.browse_button); |
||
| 3734 | if (!browseButton) { |
||
| 3735 | // browse button is required |
||
| 3736 | throw new x.DOMException(x.DOMException.NOT_FOUND_ERR); |
||
| 3737 | } |
||
| 3738 | |||
| 3739 | // figure out the options |
||
| 3740 | defaults = { |
||
| 3741 | accept: [{ |
||
| 3742 | title: I18n.translate('All Files'), |
||
| 3743 | extensions: '*' |
||
| 3744 | }], |
||
| 3745 | multiple: false, |
||
| 3746 | required_caps: false, |
||
| 3747 | container: browseButton.parentNode || document.body |
||
| 3748 | }; |
||
| 3749 | |||
| 3750 | options = Basic.extend({}, defaults, options); |
||
| 3751 | |||
| 3752 | // convert to object representation |
||
| 3753 | if (typeof(options.required_caps) === 'string') { |
||
| 3754 | options.required_caps = Runtime.parseCaps(options.required_caps); |
||
| 3755 | } |
||
| 3756 | |||
| 3757 | // normalize accept option (could be list of mime types or array of title/extensions pairs) |
||
| 3758 | if (typeof(options.accept) === 'string') { |
||
| 3759 | options.accept = Mime.mimes2extList(options.accept); |
||
| 3760 | } |
||
| 3761 | |||
| 3762 | container = Dom.get(options.container); |
||
| 3763 | // make sure we have container |
||
| 3764 | if (!container) { |
||
| 3765 | container = document.body; |
||
| 3766 | } |
||
| 3767 | |||
| 3768 | // make container relative, if it's not |
||
| 3769 | if (Dom.getStyle(container, 'position') === 'static') { |
||
| 3770 | container.style.position = 'relative'; |
||
| 3771 | } |
||
| 3772 | |||
| 3773 | container = browseButton = null; // IE |
||
| 3774 | |||
| 3775 | RuntimeClient.call(this); |
||
| 3776 | |||
| 3777 | Basic.extend(this, { |
||
| 3778 | /** |
||
| 3779 | Unique id of the component |
||
| 3780 | |||
| 3781 | @property uid |
||
| 3782 | @protected |
||
| 3783 | @readOnly |
||
| 3784 | @type {String} |
||
| 3785 | @default UID |
||
| 3786 | */ |
||
| 3787 | uid: Basic.guid('uid_'), |
||
| 3788 | |||
| 3789 | /** |
||
| 3790 | Unique id of the connected runtime, if any. |
||
| 3791 | |||
| 3792 | @property ruid |
||
| 3793 | @protected |
||
| 3794 | @type {String} |
||
| 3795 | */ |
||
| 3796 | ruid: null, |
||
| 3797 | |||
| 3798 | /** |
||
| 3799 | Unique id of the runtime container. Useful to get hold of it for various manipulations. |
||
| 3800 | |||
| 3801 | @property shimid |
||
| 3802 | @protected |
||
| 3803 | @type {String} |
||
| 3804 | */ |
||
| 3805 | shimid: null, |
||
| 3806 | |||
| 3807 | /** |
||
| 3808 | Array of selected moxie.file.File objects |
||
| 3809 | |||
| 3810 | @property files |
||
| 3811 | @type {Array} |
||
| 3812 | @default null |
||
| 3813 | */ |
||
| 3814 | files: null, |
||
| 3815 | |||
| 3816 | /** |
||
| 3817 | Initializes the file-picker, connects it to runtime and dispatches event ready when done. |
||
| 3818 | |||
| 3819 | @method init |
||
| 3820 | */ |
||
| 3821 | init: function() { |
||
| 3822 | var self = this; |
||
| 3823 | |||
| 3824 | self.bind('RuntimeInit', function(e, runtime) { |
||
| 3825 | self.ruid = runtime.uid; |
||
| 3826 | self.shimid = runtime.shimid; |
||
| 3827 | |||
| 3828 | self.bind("Ready", function() { |
||
| 3829 | self.trigger("Refresh"); |
||
| 3830 | }, 999); |
||
| 3831 | |||
| 3832 | // re-position and resize shim container |
||
| 3833 | self.bind('Refresh', function() { |
||
| 3834 | var pos, size, browseButton, shimContainer, zIndex; |
||
| 3835 | |||
| 3836 | browseButton = Dom.get(options.browse_button); |
||
| 3837 | shimContainer = Dom.get(runtime.shimid); // do not use runtime.getShimContainer(), since it will create container if it doesn't exist |
||
| 3838 | |||
| 3839 | if (browseButton) { |
||
| 3840 | pos = Dom.getPos(browseButton, Dom.get(options.container)); |
||
| 3841 | size = Dom.getSize(browseButton); |
||
| 3842 | zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 0; |
||
| 3843 | |||
| 3844 | if (shimContainer) { |
||
| 3845 | Basic.extend(shimContainer.style, { |
||
| 3846 | top: pos.y + 'px', |
||
| 3847 | left: pos.x + 'px', |
||
| 3848 | width: size.w + 'px', |
||
| 3849 | height: size.h + 'px', |
||
| 3850 | zIndex: zIndex + 1 |
||
| 3851 | }); |
||
| 3852 | } |
||
| 3853 | } |
||
| 3854 | shimContainer = browseButton = null; |
||
| 3855 | }); |
||
| 3856 | |||
| 3857 | runtime.exec.call(self, 'FileInput', 'init', options); |
||
| 3858 | }); |
||
| 3859 | |||
| 3860 | // runtime needs: options.required_features, options.runtime_order and options.container |
||
| 3861 | self.connectRuntime(Basic.extend({}, options, { |
||
| 3862 | required_caps: { |
||
| 3863 | select_file: true |
||
| 3864 | } |
||
| 3865 | })); |
||
| 3866 | }, |
||
| 3867 | |||
| 3868 | |||
| 3869 | /** |
||
| 3870 | * Get current option value by its name |
||
| 3871 | * |
||
| 3872 | * @method getOption |
||
| 3873 | * @param name |
||
| 3874 | * @return {Mixed} |
||
| 3875 | */ |
||
| 3876 | getOption: function(name) { |
||
| 3877 | return options[name]; |
||
| 3878 | }, |
||
| 3879 | |||
| 3880 | |||
| 3881 | /** |
||
| 3882 | * Sets a new value for the option specified by name |
||
| 3883 | * |
||
| 3884 | * @method setOption |
||
| 3885 | * @param name |
||
| 3886 | * @param value |
||
| 3887 | */ |
||
| 3888 | setOption: function(name, value) { |
||
| 3889 | if (!options.hasOwnProperty(name)) { |
||
| 3890 | return; |
||
| 3891 | } |
||
| 3892 | |||
| 3893 | var oldValue = options[name]; |
||
| 3894 | |||
| 3895 | switch (name) { |
||
| 3896 | case 'accept': |
||
| 3897 | if (typeof(value) === 'string') { |
||
| 3898 | value = Mime.mimes2extList(value); |
||
| 3899 | } |
||
| 3900 | break; |
||
| 3901 | |||
| 3902 | case 'container': |
||
| 3903 | case 'required_caps': |
||
| 3904 | throw new x.FileException(x.FileException.NO_MODIFICATION_ALLOWED_ERR); |
||
| 3905 | } |
||
| 3906 | |||
| 3907 | options[name] = value; |
||
| 3908 | this.exec('FileInput', 'setOption', name, value); |
||
| 3909 | |||
| 3910 | this.trigger('OptionChanged', name, value, oldValue); |
||
| 3911 | }, |
||
| 3912 | |||
| 3913 | /** |
||
| 3914 | Disables file-picker element, so that it doesn't react to mouse clicks. |
||
| 3915 | |||
| 3916 | @method disable |
||
| 3917 | @param {Boolean} [state=true] Disable component if - true, enable if - false |
||
| 3918 | */ |
||
| 3919 | disable: function(state) { |
||
| 3920 | var runtime = this.getRuntime(); |
||
| 3921 | if (runtime) { |
||
| 3922 | this.exec('FileInput', 'disable', Basic.typeOf(state) === 'undefined' ? true : state); |
||
| 3923 | } |
||
| 3924 | }, |
||
| 3925 | |||
| 3926 | |||
| 3927 | /** |
||
| 3928 | Reposition and resize dialog trigger to match the position and size of browse_button element. |
||
| 3929 | |||
| 3930 | @method refresh |
||
| 3931 | */ |
||
| 3932 | refresh: function() { |
||
| 3933 | this.trigger("Refresh"); |
||
| 3934 | }, |
||
| 3935 | |||
| 3936 | |||
| 3937 | /** |
||
| 3938 | Destroy component. |
||
| 3939 | |||
| 3940 | @method destroy |
||
| 3941 | */ |
||
| 3942 | destroy: function() { |
||
| 3943 | var runtime = this.getRuntime(); |
||
| 3944 | if (runtime) { |
||
| 3945 | runtime.exec.call(this, 'FileInput', 'destroy'); |
||
| 3946 | this.disconnectRuntime(); |
||
| 3947 | } |
||
| 3948 | |||
| 3949 | if (Basic.typeOf(this.files) === 'array') { |
||
| 3950 | // no sense in leaving associated files behind |
||
| 3951 | Basic.each(this.files, function(file) { |
||
| 3952 | file.destroy(); |
||
| 3953 | }); |
||
| 3954 | } |
||
| 3955 | this.files = null; |
||
| 3956 | |||
| 3957 | this.unbindAll(); |
||
| 3958 | } |
||
| 3959 | }); |
||
| 3960 | |||
| 3961 | this.handleEventProps(dispatches); |
||
| 3962 | } |
||
| 3963 | |||
| 3964 | FileInput.prototype = EventTarget.instance; |
||
| 3965 | |||
| 3966 | return FileInput; |
||
| 3967 | }); |
||
| 3968 | |||
| 3969 | // Included from: src/javascript/file/File.js |
||
| 3970 | |||
| 3971 | /** |
||
| 3972 | * File.js |
||
| 3973 | * |
||
| 3974 | * Copyright 2013, Moxiecode Systems AB |
||
| 3975 | * Released under GPL License. |
||
| 3976 | * |
||
| 3977 | * License: http://www.plupload.com/license |
||
| 3978 | * Contributing: http://www.plupload.com/contributing |
||
| 3979 | */ |
||
| 3980 | |||
| 3981 | define('moxie/file/File', [ |
||
| 3982 | 'moxie/core/utils/Basic', |
||
| 3983 | 'moxie/core/utils/Mime', |
||
| 3984 | 'moxie/file/Blob' |
||
| 3985 | ], function(Basic, Mime, Blob) { |
||
| 3986 | /** |
||
| 3987 | @class moxie/file/File |
||
| 3988 | @extends Blob |
||
| 3989 | @constructor |
||
| 3990 | @param {String} ruid Unique id of the runtime, to which this blob belongs to |
||
| 3991 | @param {Object} file Object "Native" file object, as it is represented in the runtime |
||
| 3992 | */ |
||
| 3993 | function File(ruid, file) { |
||
| 3994 | if (!file) { // avoid extra errors in case we overlooked something |
||
| 3995 | file = {}; |
||
| 3996 | } |
||
| 3997 | |||
| 3998 | Blob.apply(this, arguments); |
||
| 3999 | |||
| 4000 | if (!this.type) { |
||
| 4001 | this.type = Mime.getFileMime(file.name); |
||
| 4002 | } |
||
| 4003 | |||
| 4004 | // sanitize file name or generate new one |
||
| 4005 | var name; |
||
| 4006 | if (file.name) { |
||
| 4007 | name = file.name.replace(/\\/g, '/'); |
||
| 4008 | name = name.substr(name.lastIndexOf('/') + 1); |
||
| 4009 | } else if (this.type) { |
||
| 4010 | var prefix = this.type.split('/')[0]; |
||
| 4011 | name = Basic.guid((prefix !== '' ? prefix : 'file') + '_'); |
||
| 4012 | |||
| 4013 | if (Mime.extensions[this.type]) { |
||
| 4014 | name += '.' + Mime.extensions[this.type][0]; // append proper extension if possible |
||
| 4015 | } |
||
| 4016 | } |
||
| 4017 | |||
| 4018 | |||
| 4019 | Basic.extend(this, { |
||
| 4020 | /** |
||
| 4021 | File name |
||
| 4022 | |||
| 4023 | @property name |
||
| 4024 | @type {String} |
||
| 4025 | @default UID |
||
| 4026 | */ |
||
| 4027 | name: name || Basic.guid('file_'), |
||
| 4028 | |||
| 4029 | /** |
||
| 4030 | Relative path to the file inside a directory |
||
| 4031 | |||
| 4032 | @property relativePath |
||
| 4033 | @type {String} |
||
| 4034 | @default '' |
||
| 4035 | */ |
||
| 4036 | relativePath: '', |
||
| 4037 | |||
| 4038 | /** |
||
| 4039 | Date of last modification |
||
| 4040 | |||
| 4041 | @property lastModifiedDate |
||
| 4042 | @type {String} |
||
| 4043 | @default now |
||
| 4044 | */ |
||
| 4045 | lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString() // Thu Aug 23 2012 19:40:00 GMT+0400 (GET) |
||
| 4046 | }); |
||
| 4047 | } |
||
| 4048 | |||
| 4049 | File.prototype = Blob.prototype; |
||
| 4050 | |||
| 4051 | return File; |
||
| 4052 | }); |
||
| 4053 | |||
| 4054 | // Included from: src/javascript/file/FileDrop.js |
||
| 4055 | |||
| 4056 | /** |
||
| 4057 | * FileDrop.js |
||
| 4058 | * |
||
| 4059 | * Copyright 2013, Moxiecode Systems AB |
||
| 4060 | * Released under GPL License. |
||
| 4061 | * |
||
| 4062 | * License: http://www.plupload.com/license |
||
| 4063 | * Contributing: http://www.plupload.com/contributing |
||
| 4064 | */ |
||
| 4065 | |||
| 4066 | define('moxie/file/FileDrop', [ |
||
| 4067 | 'moxie/core/I18n', |
||
| 4068 | 'moxie/core/utils/Dom', |
||
| 4069 | 'moxie/core/Exceptions', |
||
| 4070 | 'moxie/core/utils/Basic', |
||
| 4071 | 'moxie/core/utils/Env', |
||
| 4072 | 'moxie/file/File', |
||
| 4073 | 'moxie/runtime/RuntimeClient', |
||
| 4074 | 'moxie/core/EventTarget', |
||
| 4075 | 'moxie/core/utils/Mime' |
||
| 4076 | ], function(I18n, Dom, x, Basic, Env, File, RuntimeClient, EventTarget, Mime) { |
||
| 4077 | /** |
||
| 4078 | Turn arbitrary DOM element to a drop zone accepting files. Converts selected files to _File_ objects, to be used |
||
| 4079 | in conjunction with _Image_, preloaded in memory with _FileReader_ or uploaded to a server through |
||
| 4080 | _XMLHttpRequest_. |
||
| 4081 | |||
| 4082 | @example |
||
| 4083 | <div id="drop_zone"> |
||
| 4084 | Drop files here |
||
| 4085 | </div> |
||
| 4086 | <br /> |
||
| 4087 | <div id="filelist"></div> |
||
| 4088 | |||
| 4089 | <script type="text/javascript"> |
||
| 4090 | var fileDrop = new moxie.file.FileDrop('drop_zone'), fileList = moxie.utils.Dom.get('filelist'); |
||
| 4091 | |||
| 4092 | fileDrop.ondrop = function() { |
||
| 4093 | moxie.utils.Basic.each(this.files, function(file) { |
||
| 4094 | fileList.innerHTML += '<div>' + file.name + '</div>'; |
||
| 4095 | }); |
||
| 4096 | }; |
||
| 4097 | |||
| 4098 | fileDrop.init(); |
||
| 4099 | </script> |
||
| 4100 | |||
| 4101 | @class moxie/file/FileDrop |
||
| 4102 | @constructor |
||
| 4103 | @extends EventTarget |
||
| 4104 | @uses RuntimeClient |
||
| 4105 | @param {Object|String} options If options has typeof string, argument is considered as options.drop_zone |
||
| 4106 | @param {String|DOMElement} options.drop_zone DOM Element to turn into a drop zone |
||
| 4107 | @param {Array} [options.accept] Array of mime types to accept. By default accepts all |
||
| 4108 | @param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support |
||
| 4109 | */ |
||
| 4110 | var dispatches = [ |
||
| 4111 | /** |
||
| 4112 | Dispatched when runtime is connected and drop zone is ready to accept files. |
||
| 4113 | |||
| 4114 | @event ready |
||
| 4115 | @param {Object} event |
||
| 4116 | */ |
||
| 4117 | 'ready', |
||
| 4118 | |||
| 4119 | /** |
||
| 4120 | Dispatched when dragging cursor enters the drop zone. |
||
| 4121 | |||
| 4122 | @event dragenter |
||
| 4123 | @param {Object} event |
||
| 4124 | */ |
||
| 4125 | 'dragenter', |
||
| 4126 | |||
| 4127 | /** |
||
| 4128 | Dispatched when dragging cursor leaves the drop zone. |
||
| 4129 | |||
| 4130 | @event dragleave |
||
| 4131 | @param {Object} event |
||
| 4132 | */ |
||
| 4133 | 'dragleave', |
||
| 4134 | |||
| 4135 | /** |
||
| 4136 | Dispatched when file is dropped onto the drop zone. |
||
| 4137 | |||
| 4138 | @event drop |
||
| 4139 | @param {Object} event |
||
| 4140 | */ |
||
| 4141 | 'drop', |
||
| 4142 | |||
| 4143 | /** |
||
| 4144 | Dispatched if error occurs. |
||
| 4145 | |||
| 4146 | @event error |
||
| 4147 | @param {Object} event |
||
| 4148 | */ |
||
| 4149 | 'error' |
||
| 4150 | ]; |
||
| 4151 | |||
| 4152 | function FileDrop(options) { |
||
| 4153 | if (MXI_DEBUG) { |
||
| 4154 | Env.log("Instantiating FileDrop..."); |
||
| 4155 | } |
||
| 4156 | |||
| 4157 | var self = this, defaults; |
||
| 4158 | |||
| 4159 | // if flat argument passed it should be drop_zone id |
||
| 4160 | if (typeof(options) === 'string') { |
||
| 4161 | options = { drop_zone : options }; |
||
| 4162 | } |
||
| 4163 | |||
| 4164 | // figure out the options |
||
| 4165 | defaults = { |
||
| 4166 | accept: [{ |
||
| 4167 | title: I18n.translate('All Files'), |
||
| 4168 | extensions: '*' |
||
| 4169 | }], |
||
| 4170 | required_caps: { |
||
| 4171 | drag_and_drop: true |
||
| 4172 | } |
||
| 4173 | }; |
||
| 4174 | |||
| 4175 | options = typeof(options) === 'object' ? Basic.extend({}, defaults, options) : defaults; |
||
| 4176 | |||
| 4177 | // this will help us to find proper default container |
||
| 4178 | options.container = Dom.get(options.drop_zone) || document.body; |
||
| 4179 | |||
| 4180 | // make container relative, if it is not |
||
| 4181 | if (Dom.getStyle(options.container, 'position') === 'static') { |
||
| 4182 | options.container.style.position = 'relative'; |
||
| 4183 | } |
||
| 4184 | |||
| 4185 | // normalize accept option (could be list of mime types or array of title/extensions pairs) |
||
| 4186 | if (typeof(options.accept) === 'string') { |
||
| 4187 | options.accept = Mime.mimes2extList(options.accept); |
||
| 4188 | } |
||
| 4189 | |||
| 4190 | RuntimeClient.call(self); |
||
| 4191 | |||
| 4192 | Basic.extend(self, { |
||
| 4193 | uid: Basic.guid('uid_'), |
||
| 4194 | |||
| 4195 | ruid: null, |
||
| 4196 | |||
| 4197 | files: null, |
||
| 4198 | |||
| 4199 | init: function() { |
||
| 4200 | self.bind('RuntimeInit', function(e, runtime) { |
||
| 4201 | self.ruid = runtime.uid; |
||
| 4202 | runtime.exec.call(self, 'FileDrop', 'init', options); |
||
| 4203 | self.dispatchEvent('ready'); |
||
| 4204 | }); |
||
| 4205 | |||
| 4206 | // runtime needs: options.required_features, options.runtime_order and options.container |
||
| 4207 | self.connectRuntime(options); // throws RuntimeError |
||
| 4208 | }, |
||
| 4209 | |||
| 4210 | destroy: function() { |
||
| 4211 | var runtime = this.getRuntime(); |
||
| 4212 | if (runtime) { |
||
| 4213 | runtime.exec.call(this, 'FileDrop', 'destroy'); |
||
| 4214 | this.disconnectRuntime(); |
||
| 4215 | } |
||
| 4216 | this.files = null; |
||
| 4217 | |||
| 4218 | this.unbindAll(); |
||
| 4219 | } |
||
| 4220 | }); |
||
| 4221 | |||
| 4222 | this.handleEventProps(dispatches); |
||
| 4223 | } |
||
| 4224 | |||
| 4225 | FileDrop.prototype = EventTarget.instance; |
||
| 4226 | |||
| 4227 | return FileDrop; |
||
| 4228 | }); |
||
| 4229 | |||
| 4230 | // Included from: src/javascript/file/FileReader.js |
||
| 4231 | |||
| 4232 | /** |
||
| 4233 | * FileReader.js |
||
| 4234 | * |
||
| 4235 | * Copyright 2013, Moxiecode Systems AB |
||
| 4236 | * Released under GPL License. |
||
| 4237 | * |
||
| 4238 | * License: http://www.plupload.com/license |
||
| 4239 | * Contributing: http://www.plupload.com/contributing |
||
| 4240 | */ |
||
| 4241 | |||
| 4242 | define('moxie/file/FileReader', [ |
||
| 4243 | 'moxie/core/utils/Basic', |
||
| 4244 | 'moxie/core/utils/Encode', |
||
| 4245 | 'moxie/core/Exceptions', |
||
| 4246 | 'moxie/core/EventTarget', |
||
| 4247 | 'moxie/file/Blob', |
||
| 4248 | 'moxie/runtime/RuntimeClient' |
||
| 4249 | ], function(Basic, Encode, x, EventTarget, Blob, RuntimeClient) { |
||
| 4250 | /** |
||
| 4251 | Utility for preloading o.Blob/o.File objects in memory. By design closely follows [W3C FileReader](http://www.w3.org/TR/FileAPI/#dfn-filereader) |
||
| 4252 | interface. Where possible uses native FileReader, where - not falls back to shims. |
||
| 4253 | |||
| 4254 | @class moxie/file/FileReader |
||
| 4255 | @constructor FileReader |
||
| 4256 | @extends EventTarget |
||
| 4257 | @uses RuntimeClient |
||
| 4258 | */ |
||
| 4259 | var dispatches = [ |
||
| 4260 | |||
| 4261 | /** |
||
| 4262 | Dispatched when the read starts. |
||
| 4263 | |||
| 4264 | @event loadstart |
||
| 4265 | @param {Object} event |
||
| 4266 | */ |
||
| 4267 | 'loadstart', |
||
| 4268 | |||
| 4269 | /** |
||
| 4270 | Dispatched while reading (and decoding) blob, and reporting partial Blob data (progess.loaded/progress.total). |
||
| 4271 | |||
| 4272 | @event progress |
||
| 4273 | @param {Object} event |
||
| 4274 | */ |
||
| 4275 | 'progress', |
||
| 4276 | |||
| 4277 | /** |
||
| 4278 | Dispatched when the read has successfully completed. |
||
| 4279 | |||
| 4280 | @event load |
||
| 4281 | @param {Object} event |
||
| 4282 | */ |
||
| 4283 | 'load', |
||
| 4284 | |||
| 4285 | /** |
||
| 4286 | Dispatched when the read has been aborted. For instance, by invoking the abort() method. |
||
| 4287 | |||
| 4288 | @event abort |
||
| 4289 | @param {Object} event |
||
| 4290 | */ |
||
| 4291 | 'abort', |
||
| 4292 | |||
| 4293 | /** |
||
| 4294 | Dispatched when the read has failed. |
||
| 4295 | |||
| 4296 | @event error |
||
| 4297 | @param {Object} event |
||
| 4298 | */ |
||
| 4299 | 'error', |
||
| 4300 | |||
| 4301 | /** |
||
| 4302 | Dispatched when the request has completed (either in success or failure). |
||
| 4303 | |||
| 4304 | @event loadend |
||
| 4305 | @param {Object} event |
||
| 4306 | */ |
||
| 4307 | 'loadend' |
||
| 4308 | ]; |
||
| 4309 | |||
| 4310 | function FileReader() { |
||
| 4311 | |||
| 4312 | RuntimeClient.call(this); |
||
| 4313 | |||
| 4314 | Basic.extend(this, { |
||
| 4315 | /** |
||
| 4316 | UID of the component instance. |
||
| 4317 | |||
| 4318 | @property uid |
||
| 4319 | @type {String} |
||
| 4320 | */ |
||
| 4321 | uid: Basic.guid('uid_'), |
||
| 4322 | |||
| 4323 | /** |
||
| 4324 | Contains current state of FileReader object. Can take values of FileReader.EMPTY, FileReader.LOADING |
||
| 4325 | and FileReader.DONE. |
||
| 4326 | |||
| 4327 | @property readyState |
||
| 4328 | @type {Number} |
||
| 4329 | @default FileReader.EMPTY |
||
| 4330 | */ |
||
| 4331 | readyState: FileReader.EMPTY, |
||
| 4332 | |||
| 4333 | /** |
||
| 4334 | Result of the successful read operation. |
||
| 4335 | |||
| 4336 | @property result |
||
| 4337 | @type {String} |
||
| 4338 | */ |
||
| 4339 | result: null, |
||
| 4340 | |||
| 4341 | /** |
||
| 4342 | Stores the error of failed asynchronous read operation. |
||
| 4343 | |||
| 4344 | @property error |
||
| 4345 | @type {DOMError} |
||
| 4346 | */ |
||
| 4347 | error: null, |
||
| 4348 | |||
| 4349 | /** |
||
| 4350 | Initiates reading of File/Blob object contents to binary string. |
||
| 4351 | |||
| 4352 | @method readAsBinaryString |
||
| 4353 | @param {Blob|File} blob Object to preload |
||
| 4354 | */ |
||
| 4355 | readAsBinaryString: function(blob) { |
||
| 4356 | _read.call(this, 'readAsBinaryString', blob); |
||
| 4357 | }, |
||
| 4358 | |||
| 4359 | /** |
||
| 4360 | Initiates reading of File/Blob object contents to dataURL string. |
||
| 4361 | |||
| 4362 | @method readAsDataURL |
||
| 4363 | @param {Blob|File} blob Object to preload |
||
| 4364 | */ |
||
| 4365 | readAsDataURL: function(blob) { |
||
| 4366 | _read.call(this, 'readAsDataURL', blob); |
||
| 4367 | }, |
||
| 4368 | |||
| 4369 | /** |
||
| 4370 | Initiates reading of File/Blob object contents to string. |
||
| 4371 | |||
| 4372 | @method readAsText |
||
| 4373 | @param {Blob|File} blob Object to preload |
||
| 4374 | */ |
||
| 4375 | readAsText: function(blob) { |
||
| 4376 | _read.call(this, 'readAsText', blob); |
||
| 4377 | }, |
||
| 4378 | |||
| 4379 | /** |
||
| 4380 | Aborts preloading process. |
||
| 4381 | |||
| 4382 | @method abort |
||
| 4383 | */ |
||
| 4384 | abort: function() { |
||
| 4385 | this.result = null; |
||
| 4386 | |||
| 4387 | if (Basic.inArray(this.readyState, [FileReader.EMPTY, FileReader.DONE]) !== -1) { |
||
| 4388 | return; |
||
| 4389 | } else if (this.readyState === FileReader.LOADING) { |
||
| 4390 | this.readyState = FileReader.DONE; |
||
| 4391 | } |
||
| 4392 | |||
| 4393 | this.exec('FileReader', 'abort'); |
||
| 4394 | |||
| 4395 | this.trigger('abort'); |
||
| 4396 | this.trigger('loadend'); |
||
| 4397 | }, |
||
| 4398 | |||
| 4399 | /** |
||
| 4400 | Destroy component and release resources. |
||
| 4401 | |||
| 4402 | @method destroy |
||
| 4403 | */ |
||
| 4404 | destroy: function() { |
||
| 4405 | this.abort(); |
||
| 4406 | this.exec('FileReader', 'destroy'); |
||
| 4407 | this.disconnectRuntime(); |
||
| 4408 | this.unbindAll(); |
||
| 4409 | } |
||
| 4410 | }); |
||
| 4411 | |||
| 4412 | // uid must already be assigned |
||
| 4413 | this.handleEventProps(dispatches); |
||
| 4414 | |||
| 4415 | this.bind('Error', function(e, err) { |
||
| 4416 | this.readyState = FileReader.DONE; |
||
| 4417 | this.error = err; |
||
| 4418 | }, 999); |
||
| 4419 | |||
| 4420 | this.bind('Load', function(e) { |
||
| 4421 | this.readyState = FileReader.DONE; |
||
| 4422 | }, 999); |
||
| 4423 | |||
| 4424 | |||
| 4425 | function _read(op, blob) { |
||
| 4426 | var self = this; |
||
| 4427 | |||
| 4428 | this.trigger('loadstart'); |
||
| 4429 | |||
| 4430 | if (this.readyState === FileReader.LOADING) { |
||
| 4431 | this.trigger('error', new x.DOMException(x.DOMException.INVALID_STATE_ERR)); |
||
| 4432 | this.trigger('loadend'); |
||
| 4433 | return; |
||
| 4434 | } |
||
| 4435 | |||
| 4436 | // if source is not o.Blob/o.File |
||
| 4437 | if (!(blob instanceof Blob)) { |
||
| 4438 | this.trigger('error', new x.DOMException(x.DOMException.NOT_FOUND_ERR)); |
||
| 4439 | this.trigger('loadend'); |
||
| 4440 | return; |
||
| 4441 | } |
||
| 4442 | |||
| 4443 | this.result = null; |
||
| 4444 | this.readyState = FileReader.LOADING; |
||
| 4445 | |||
| 4446 | if (blob.isDetached()) { |
||
| 4447 | var src = blob.getSource(); |
||
| 4448 | switch (op) { |
||
| 4449 | case 'readAsText': |
||
| 4450 | case 'readAsBinaryString': |
||
| 4451 | this.result = src; |
||
| 4452 | break; |
||
| 4453 | case 'readAsDataURL': |
||
| 4454 | this.result = 'data:' + blob.type + ';base64,' + Encode.btoa(src); |
||
| 4455 | break; |
||
| 4456 | } |
||
| 4457 | this.readyState = FileReader.DONE; |
||
| 4458 | this.trigger('load'); |
||
| 4459 | this.trigger('loadend'); |
||
| 4460 | } else { |
||
| 4461 | this.connectRuntime(blob.ruid); |
||
| 4462 | this.exec('FileReader', 'read', op, blob); |
||
| 4463 | } |
||
| 4464 | } |
||
| 4465 | } |
||
| 4466 | |||
| 4467 | /** |
||
| 4468 | Initial FileReader state |
||
| 4469 | |||
| 4470 | @property EMPTY |
||
| 4471 | @type {Number} |
||
| 4472 | @final |
||
| 4473 | @static |
||
| 4474 | @default 0 |
||
| 4475 | */ |
||
| 4476 | FileReader.EMPTY = 0; |
||
| 4477 | |||
| 4478 | /** |
||
| 4479 | FileReader switches to this state when it is preloading the source |
||
| 4480 | |||
| 4481 | @property LOADING |
||
| 4482 | @type {Number} |
||
| 4483 | @final |
||
| 4484 | @static |
||
| 4485 | @default 1 |
||
| 4486 | */ |
||
| 4487 | FileReader.LOADING = 1; |
||
| 4488 | |||
| 4489 | /** |
||
| 4490 | Preloading is complete, this is a final state |
||
| 4491 | |||
| 4492 | @property DONE |
||
| 4493 | @type {Number} |
||
| 4494 | @final |
||
| 4495 | @static |
||
| 4496 | @default 2 |
||
| 4497 | */ |
||
| 4498 | FileReader.DONE = 2; |
||
| 4499 | |||
| 4500 | FileReader.prototype = EventTarget.instance; |
||
| 4501 | |||
| 4502 | return FileReader; |
||
| 4503 | }); |
||
| 4504 | |||
| 4505 | // Included from: src/javascript/core/utils/Url.js |
||
| 4506 | |||
| 4507 | /** |
||
| 4508 | * Url.js |
||
| 4509 | * |
||
| 4510 | * Copyright 2013, Moxiecode Systems AB |
||
| 4511 | * Released under GPL License. |
||
| 4512 | * |
||
| 4513 | * License: http://www.plupload.com/license |
||
| 4514 | * Contributing: http://www.plupload.com/contributing |
||
| 4515 | */ |
||
| 4516 | |||
| 4517 | /** |
||
| 4518 | @class moxie/core/utils/Url |
||
| 4519 | @public |
||
| 4520 | @static |
||
| 4521 | */ |
||
| 4522 | |||
| 4523 | define('moxie/core/utils/Url', [ |
||
| 4524 | 'moxie/core/utils/Basic' |
||
| 4525 | ], function(Basic) { |
||
| 4526 | /** |
||
| 4527 | Parse url into separate components and fill in absent parts with parts from current url, |
||
| 4528 | based on https://raw.github.com/kvz/phpjs/master/functions/url/parse_url.js |
||
| 4529 | |||
| 4530 | @method parseUrl |
||
| 4531 | @static |
||
| 4532 | @param {String} url Url to parse (defaults to empty string if undefined) |
||
| 4533 | @return {Object} Hash containing extracted uri components |
||
| 4534 | */ |
||
| 4535 | var parseUrl = function(url, currentUrl) { |
||
| 4536 | var key = ['source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'] |
||
| 4537 | , i = key.length |
||
| 4538 | , ports = { |
||
| 4539 | http: 80, |
||
| 4540 | https: 443 |
||
| 4541 | } |
||
| 4542 | , uri = {} |
||
| 4543 | , regex = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@\/]*):?([^:@\/]*))?@)?(\[[\da-fA-F:]+\]|[^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/ |
||
| 4544 | , m = regex.exec(url || '') |
||
| 4545 | , isRelative |
||
| 4546 | , isSchemeLess = /^\/\/\w/.test(url) |
||
| 4547 | ; |
||
| 4548 | |||
| 4549 | switch (Basic.typeOf(currentUrl)) { |
||
| 4550 | case 'undefined': |
||
| 4551 | currentUrl = parseUrl(document.location.href, false); |
||
| 4552 | break; |
||
| 4553 | |||
| 4554 | case 'string': |
||
| 4555 | currentUrl = parseUrl(currentUrl, false); |
||
| 4556 | break; |
||
| 4557 | } |
||
| 4558 | |||
| 4559 | while (i--) { |
||
| 4560 | if (m[i]) { |
||
| 4561 | uri[key[i]] = m[i]; |
||
| 4562 | } |
||
| 4563 | } |
||
| 4564 | |||
| 4565 | isRelative = !isSchemeLess && !uri.scheme; |
||
| 4566 | |||
| 4567 | if (isSchemeLess || isRelative) { |
||
| 4568 | uri.scheme = currentUrl.scheme; |
||
| 4569 | } |
||
| 4570 | |||
| 4571 | // when url is relative, we set the origin and the path ourselves |
||
| 4572 | if (isRelative) { |
||
| 4573 | uri.host = currentUrl.host; |
||
| 4574 | uri.port = currentUrl.port; |
||
| 4575 | |||
| 4576 | var path = ''; |
||
| 4577 | // for urls without trailing slash we need to figure out the path |
||
| 4578 | if (/^[^\/]/.test(uri.path)) { |
||
| 4579 | path = currentUrl.path; |
||
| 4580 | // if path ends with a filename, strip it |
||
| 4581 | if (/\/[^\/]*\.[^\/]*$/.test(path)) { |
||
| 4582 | path = path.replace(/\/[^\/]+$/, '/'); |
||
| 4583 | } else { |
||
| 4584 | // avoid double slash at the end (see #127) |
||
| 4585 | path = path.replace(/\/?$/, '/'); |
||
| 4586 | } |
||
| 4587 | } |
||
| 4588 | uri.path = path + (uri.path || ''); // site may reside at domain.com or domain.com/subdir |
||
| 4589 | } |
||
| 4590 | |||
| 4591 | if (!uri.port) { |
||
| 4592 | uri.port = ports[uri.scheme] || 80; |
||
| 4593 | } |
||
| 4594 | |||
| 4595 | uri.port = parseInt(uri.port, 10); |
||
| 4596 | |||
| 4597 | if (!uri.path) { |
||
| 4598 | uri.path = "/"; |
||
| 4599 | } |
||
| 4600 | |||
| 4601 | delete uri.source; |
||
| 4602 | |||
| 4603 | return uri; |
||
| 4604 | }; |
||
| 4605 | |||
| 4606 | /** |
||
| 4607 | Resolve url - among other things will turn relative url to absolute |
||
| 4608 | |||
| 4609 | @method resolveUrl |
||
| 4610 | @static |
||
| 4611 | @param {String|Object} url Either absolute or relative, or a result of parseUrl call |
||
| 4612 | @return {String} Resolved, absolute url |
||
| 4613 | */ |
||
| 4614 | var resolveUrl = function(url) { |
||
| 4615 | var ports = { // we ignore default ports |
||
| 4616 | http: 80, |
||
| 4617 | https: 443 |
||
| 4618 | } |
||
| 4619 | , urlp = typeof(url) === 'object' ? url : parseUrl(url); |
||
| 4620 | ; |
||
| 4621 | |||
| 4622 | return urlp.scheme + '://' + urlp.host + (urlp.port !== ports[urlp.scheme] ? ':' + urlp.port : '') + urlp.path + (urlp.query ? urlp.query : ''); |
||
| 4623 | }; |
||
| 4624 | |||
| 4625 | /** |
||
| 4626 | Check if specified url has the same origin as the current document |
||
| 4627 | |||
| 4628 | @method hasSameOrigin |
||
| 4629 | @static |
||
| 4630 | @param {String|Object} url |
||
| 4631 | @return {Boolean} |
||
| 4632 | */ |
||
| 4633 | var hasSameOrigin = function(url) { |
||
| 4634 | function origin(url) { |
||
| 4635 | return [url.scheme, url.host, url.port].join('/'); |
||
| 4636 | } |
||
| 4637 | |||
| 4638 | if (typeof url === 'string') { |
||
| 4639 | url = parseUrl(url); |
||
| 4640 | } |
||
| 4641 | |||
| 4642 | return origin(parseUrl()) === origin(url); |
||
| 4643 | }; |
||
| 4644 | |||
| 4645 | return { |
||
| 4646 | parseUrl: parseUrl, |
||
| 4647 | resolveUrl: resolveUrl, |
||
| 4648 | hasSameOrigin: hasSameOrigin |
||
| 4649 | }; |
||
| 4650 | }); |
||
| 4651 | |||
| 4652 | // Included from: src/javascript/runtime/RuntimeTarget.js |
||
| 4653 | |||
| 4654 | /** |
||
| 4655 | * RuntimeTarget.js |
||
| 4656 | * |
||
| 4657 | * Copyright 2013, Moxiecode Systems AB |
||
| 4658 | * Released under GPL License. |
||
| 4659 | * |
||
| 4660 | * License: http://www.plupload.com/license |
||
| 4661 | * Contributing: http://www.plupload.com/contributing |
||
| 4662 | */ |
||
| 4663 | |||
| 4664 | define('moxie/runtime/RuntimeTarget', [ |
||
| 4665 | 'moxie/core/utils/Basic', |
||
| 4666 | 'moxie/runtime/RuntimeClient', |
||
| 4667 | "moxie/core/EventTarget" |
||
| 4668 | ], function(Basic, RuntimeClient, EventTarget) { |
||
| 4669 | /** |
||
| 4670 | Instance of this class can be used as a target for the events dispatched by shims, |
||
| 4671 | when allowing them onto components is for either reason inappropriate |
||
| 4672 | |||
| 4673 | @class moxie/runtime/RuntimeTarget |
||
| 4674 | @constructor |
||
| 4675 | @protected |
||
| 4676 | @extends EventTarget |
||
| 4677 | */ |
||
| 4678 | function RuntimeTarget() { |
||
| 4679 | this.uid = Basic.guid('uid_'); |
||
| 4680 | |||
| 4681 | RuntimeClient.call(this); |
||
| 4682 | |||
| 4683 | this.destroy = function() { |
||
| 4684 | this.disconnectRuntime(); |
||
| 4685 | this.unbindAll(); |
||
| 4686 | }; |
||
| 4687 | } |
||
| 4688 | |||
| 4689 | RuntimeTarget.prototype = EventTarget.instance; |
||
| 4690 | |||
| 4691 | return RuntimeTarget; |
||
| 4692 | }); |
||
| 4693 | |||
| 4694 | // Included from: src/javascript/file/FileReaderSync.js |
||
| 4695 | |||
| 4696 | /** |
||
| 4697 | * FileReaderSync.js |
||
| 4698 | * |
||
| 4699 | * Copyright 2013, Moxiecode Systems AB |
||
| 4700 | * Released under GPL License. |
||
| 4701 | * |
||
| 4702 | * License: http://www.plupload.com/license |
||
| 4703 | * Contributing: http://www.plupload.com/contributing |
||
| 4704 | */ |
||
| 4705 | |||
| 4706 | define('moxie/file/FileReaderSync', [ |
||
| 4707 | 'moxie/core/utils/Basic', |
||
| 4708 | 'moxie/runtime/RuntimeClient', |
||
| 4709 | 'moxie/core/utils/Encode' |
||
| 4710 | ], function(Basic, RuntimeClient, Encode) { |
||
| 4711 | /** |
||
| 4712 | Synchronous FileReader implementation. Something like this is available in WebWorkers environment, here |
||
| 4713 | it can be used to read only preloaded blobs/files and only below certain size (not yet sure what that'd be, |
||
| 4714 | but probably < 1mb). Not meant to be used directly by user. |
||
| 4715 | |||
| 4716 | @class moxie/file/FileReaderSync |
||
| 4717 | @private |
||
| 4718 | @constructor |
||
| 4719 | */ |
||
| 4720 | return function() { |
||
| 4721 | RuntimeClient.call(this); |
||
| 4722 | |||
| 4723 | Basic.extend(this, { |
||
| 4724 | uid: Basic.guid('uid_'), |
||
| 4725 | |||
| 4726 | readAsBinaryString: function(blob) { |
||
| 4727 | return _read.call(this, 'readAsBinaryString', blob); |
||
| 4728 | }, |
||
| 4729 | |||
| 4730 | readAsDataURL: function(blob) { |
||
| 4731 | return _read.call(this, 'readAsDataURL', blob); |
||
| 4732 | }, |
||
| 4733 | |||
| 4734 | /*readAsArrayBuffer: function(blob) { |
||
| 4735 | return _read.call(this, 'readAsArrayBuffer', blob); |
||
| 4736 | },*/ |
||
| 4737 | |||
| 4738 | readAsText: function(blob) { |
||
| 4739 | return _read.call(this, 'readAsText', blob); |
||
| 4740 | } |
||
| 4741 | }); |
||
| 4742 | |||
| 4743 | function _read(op, blob) { |
||
| 4744 | if (blob.isDetached()) { |
||
| 4745 | var src = blob.getSource(); |
||
| 4746 | switch (op) { |
||
| 4747 | case 'readAsBinaryString': |
||
| 4748 | return src; |
||
| 4749 | case 'readAsDataURL': |
||
| 4750 | return 'data:' + blob.type + ';base64,' + Encode.btoa(src); |
||
| 4751 | case 'readAsText': |
||
| 4752 | var txt = ''; |
||
| 4753 | for (var i = 0, length = src.length; i < length; i++) { |
||
| 4754 | txt += String.fromCharCode(src[i]); |
||
| 4755 | } |
||
| 4756 | return txt; |
||
| 4757 | } |
||
| 4758 | } else { |
||
| 4759 | var result = this.connectRuntime(blob.ruid).exec.call(this, 'FileReaderSync', 'read', op, blob); |
||
| 4760 | this.disconnectRuntime(); |
||
| 4761 | return result; |
||
| 4762 | } |
||
| 4763 | } |
||
| 4764 | }; |
||
| 4765 | }); |
||
| 4766 | |||
| 4767 | // Included from: src/javascript/xhr/FormData.js |
||
| 4768 | |||
| 4769 | /** |
||
| 4770 | * FormData.js |
||
| 4771 | * |
||
| 4772 | * Copyright 2013, Moxiecode Systems AB |
||
| 4773 | * Released under GPL License. |
||
| 4774 | * |
||
| 4775 | * License: http://www.plupload.com/license |
||
| 4776 | * Contributing: http://www.plupload.com/contributing |
||
| 4777 | */ |
||
| 4778 | |||
| 4779 | define("moxie/xhr/FormData", [ |
||
| 4780 | "moxie/core/Exceptions", |
||
| 4781 | "moxie/core/utils/Basic", |
||
| 4782 | "moxie/file/Blob" |
||
| 4783 | ], function(x, Basic, Blob) { |
||
| 4784 | /** |
||
| 4785 | FormData |
||
| 4786 | |||
| 4787 | @class moxie/xhr/FormData |
||
| 4788 | @constructor |
||
| 4789 | */ |
||
| 4790 | function FormData() { |
||
| 4791 | var _blob, _fields = []; |
||
| 4792 | |||
| 4793 | Basic.extend(this, { |
||
| 4794 | /** |
||
| 4795 | Append another key-value pair to the FormData object |
||
| 4796 | |||
| 4797 | @method append |
||
| 4798 | @param {String} name Name for the new field |
||
| 4799 | @param {String|Blob|Array|Object} value Value for the field |
||
| 4800 | */ |
||
| 4801 | append: function(name, value) { |
||
| 4802 | var self = this, valueType = Basic.typeOf(value); |
||
| 4803 | |||
| 4804 | // according to specs value might be either Blob or String |
||
| 4805 | if (value instanceof Blob) { |
||
| 4806 | _blob = { |
||
| 4807 | name: name, |
||
| 4808 | value: value // unfortunately we can only send single Blob in one FormData |
||
| 4809 | }; |
||
| 4810 | } else if ('array' === valueType) { |
||
| 4811 | name += '[]'; |
||
| 4812 | |||
| 4813 | Basic.each(value, function(value) { |
||
| 4814 | self.append(name, value); |
||
| 4815 | }); |
||
| 4816 | } else if ('object' === valueType) { |
||
| 4817 | Basic.each(value, function(value, key) { |
||
| 4818 | self.append(name + '[' + key + ']', value); |
||
| 4819 | }); |
||
| 4820 | } else if ('null' === valueType || 'undefined' === valueType || 'number' === valueType && isNaN(value)) { |
||
| 4821 | self.append(name, "false"); |
||
| 4822 | } else { |
||
| 4823 | _fields.push({ |
||
| 4824 | name: name, |
||
| 4825 | value: value.toString() |
||
| 4826 | }); |
||
| 4827 | } |
||
| 4828 | }, |
||
| 4829 | |||
| 4830 | /** |
||
| 4831 | Checks if FormData contains Blob. |
||
| 4832 | |||
| 4833 | @method hasBlob |
||
| 4834 | @return {Boolean} |
||
| 4835 | */ |
||
| 4836 | hasBlob: function() { |
||
| 4837 | return !!this.getBlob(); |
||
| 4838 | }, |
||
| 4839 | |||
| 4840 | /** |
||
| 4841 | Retrieves blob. |
||
| 4842 | |||
| 4843 | @method getBlob |
||
| 4844 | @return {Object} Either Blob if found or null |
||
| 4845 | */ |
||
| 4846 | getBlob: function() { |
||
| 4847 | return _blob && _blob.value || null; |
||
| 4848 | }, |
||
| 4849 | |||
| 4850 | /** |
||
| 4851 | Retrieves blob field name. |
||
| 4852 | |||
| 4853 | @method getBlobName |
||
| 4854 | @return {String} Either Blob field name or null |
||
| 4855 | */ |
||
| 4856 | getBlobName: function() { |
||
| 4857 | return _blob && _blob.name || null; |
||
| 4858 | }, |
||
| 4859 | |||
| 4860 | /** |
||
| 4861 | Loop over the fields in FormData and invoke the callback for each of them. |
||
| 4862 | |||
| 4863 | @method each |
||
| 4864 | @param {Function} cb Callback to call for each field |
||
| 4865 | */ |
||
| 4866 | each: function(cb) { |
||
| 4867 | Basic.each(_fields, function(field) { |
||
| 4868 | cb(field.value, field.name); |
||
| 4869 | }); |
||
| 4870 | |||
| 4871 | if (_blob) { |
||
| 4872 | cb(_blob.value, _blob.name); |
||
| 4873 | } |
||
| 4874 | }, |
||
| 4875 | |||
| 4876 | destroy: function() { |
||
| 4877 | _blob = null; |
||
| 4878 | _fields = []; |
||
| 4879 | } |
||
| 4880 | }); |
||
| 4881 | } |
||
| 4882 | |||
| 4883 | return FormData; |
||
| 4884 | }); |
||
| 4885 | |||
| 4886 | // Included from: src/javascript/xhr/XMLHttpRequest.js |
||
| 4887 | |||
| 4888 | /** |
||
| 4889 | * XMLHttpRequest.js |
||
| 4890 | * |
||
| 4891 | * Copyright 2013, Moxiecode Systems AB |
||
| 4892 | * Released under GPL License. |
||
| 4893 | * |
||
| 4894 | * License: http://www.plupload.com/license |
||
| 4895 | * Contributing: http://www.plupload.com/contributing |
||
| 4896 | */ |
||
| 4897 | |||
| 4898 | define("moxie/xhr/XMLHttpRequest", [ |
||
| 4899 | "moxie/core/utils/Basic", |
||
| 4900 | "moxie/core/Exceptions", |
||
| 4901 | "moxie/core/EventTarget", |
||
| 4902 | "moxie/core/utils/Encode", |
||
| 4903 | "moxie/core/utils/Url", |
||
| 4904 | "moxie/runtime/Runtime", |
||
| 4905 | "moxie/runtime/RuntimeTarget", |
||
| 4906 | "moxie/file/Blob", |
||
| 4907 | "moxie/file/FileReaderSync", |
||
| 4908 | "moxie/xhr/FormData", |
||
| 4909 | "moxie/core/utils/Env", |
||
| 4910 | "moxie/core/utils/Mime" |
||
| 4911 | ], function(Basic, x, EventTarget, Encode, Url, Runtime, RuntimeTarget, Blob, FileReaderSync, FormData, Env, Mime) { |
||
| 4912 | |||
| 4913 | var httpCode = { |
||
| 4914 | 100: 'Continue', |
||
| 4915 | 101: 'Switching Protocols', |
||
| 4916 | 102: 'Processing', |
||
| 4917 | |||
| 4918 | 200: 'OK', |
||
| 4919 | 201: 'Created', |
||
| 4920 | 202: 'Accepted', |
||
| 4921 | 203: 'Non-Authoritative Information', |
||
| 4922 | 204: 'No Content', |
||
| 4923 | 205: 'Reset Content', |
||
| 4924 | 206: 'Partial Content', |
||
| 4925 | 207: 'Multi-Status', |
||
| 4926 | 226: 'IM Used', |
||
| 4927 | |||
| 4928 | 300: 'Multiple Choices', |
||
| 4929 | 301: 'Moved Permanently', |
||
| 4930 | 302: 'Found', |
||
| 4931 | 303: 'See Other', |
||
| 4932 | 304: 'Not Modified', |
||
| 4933 | 305: 'Use Proxy', |
||
| 4934 | 306: 'Reserved', |
||
| 4935 | 307: 'Temporary Redirect', |
||
| 4936 | |||
| 4937 | 400: 'Bad Request', |
||
| 4938 | 401: 'Unauthorized', |
||
| 4939 | 402: 'Payment Required', |
||
| 4940 | 403: 'Forbidden', |
||
| 4941 | 404: 'Not Found', |
||
| 4942 | 405: 'Method Not Allowed', |
||
| 4943 | 406: 'Not Acceptable', |
||
| 4944 | 407: 'Proxy Authentication Required', |
||
| 4945 | 408: 'Request Timeout', |
||
| 4946 | 409: 'Conflict', |
||
| 4947 | 410: 'Gone', |
||
| 4948 | 411: 'Length Required', |
||
| 4949 | 412: 'Precondition Failed', |
||
| 4950 | 413: 'Request Entity Too Large', |
||
| 4951 | 414: 'Request-URI Too Long', |
||
| 4952 | 415: 'Unsupported Media Type', |
||
| 4953 | 416: 'Requested Range Not Satisfiable', |
||
| 4954 | 417: 'Expectation Failed', |
||
| 4955 | 422: 'Unprocessable Entity', |
||
| 4956 | 423: 'Locked', |
||
| 4957 | 424: 'Failed Dependency', |
||
| 4958 | 426: 'Upgrade Required', |
||
| 4959 | |||
| 4960 | 500: 'Internal Server Error', |
||
| 4961 | 501: 'Not Implemented', |
||
| 4962 | 502: 'Bad Gateway', |
||
| 4963 | 503: 'Service Unavailable', |
||
| 4964 | 504: 'Gateway Timeout', |
||
| 4965 | 505: 'HTTP Version Not Supported', |
||
| 4966 | 506: 'Variant Also Negotiates', |
||
| 4967 | 507: 'Insufficient Storage', |
||
| 4968 | 510: 'Not Extended' |
||
| 4969 | }; |
||
| 4970 | |||
| 4971 | function XMLHttpRequestUpload() { |
||
| 4972 | this.uid = Basic.guid('uid_'); |
||
| 4973 | } |
||
| 4974 | |||
| 4975 | XMLHttpRequestUpload.prototype = EventTarget.instance; |
||
| 4976 | |||
| 4977 | /** |
||
| 4978 | Implementation of XMLHttpRequest |
||
| 4979 | |||
| 4980 | @class moxie/xhr/XMLHttpRequest |
||
| 4981 | @constructor |
||
| 4982 | @uses RuntimeClient |
||
| 4983 | @extends EventTarget |
||
| 4984 | */ |
||
| 4985 | var dispatches = [ |
||
| 4986 | 'loadstart', |
||
| 4987 | |||
| 4988 | 'progress', |
||
| 4989 | |||
| 4990 | 'abort', |
||
| 4991 | |||
| 4992 | 'error', |
||
| 4993 | |||
| 4994 | 'load', |
||
| 4995 | |||
| 4996 | 'timeout', |
||
| 4997 | |||
| 4998 | 'loadend' |
||
| 4999 | |||
| 5000 | // readystatechange (for historical reasons) |
||
| 5001 | ]; |
||
| 5002 | |||
| 5003 | var NATIVE = 1, RUNTIME = 2; |
||
| 5004 | |||
| 5005 | function XMLHttpRequest() { |
||
| 5006 | var self = this, |
||
| 5007 | // this (together with _p() @see below) is here to gracefully upgrade to setter/getter syntax where possible |
||
| 5008 | props = { |
||
| 5009 | /** |
||
| 5010 | The amount of milliseconds a request can take before being terminated. Initially zero. Zero means there is no timeout. |
||
| 5011 | |||
| 5012 | @property timeout |
||
| 5013 | @type Number |
||
| 5014 | @default 0 |
||
| 5015 | */ |
||
| 5016 | timeout: 0, |
||
| 5017 | |||
| 5018 | /** |
||
| 5019 | Current state, can take following values: |
||
| 5020 | UNSENT (numeric value 0) |
||
| 5021 | The object has been constructed. |
||
| 5022 | |||
| 5023 | OPENED (numeric value 1) |
||
| 5024 | The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method. |
||
| 5025 | |||
| 5026 | HEADERS_RECEIVED (numeric value 2) |
||
| 5027 | All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available. |
||
| 5028 | |||
| 5029 | LOADING (numeric value 3) |
||
| 5030 | The response entity body is being received. |
||
| 5031 | |||
| 5032 | DONE (numeric value 4) |
||
| 5033 | |||
| 5034 | @property readyState |
||
| 5035 | @type Number |
||
| 5036 | @default 0 (UNSENT) |
||
| 5037 | */ |
||
| 5038 | readyState: XMLHttpRequest.UNSENT, |
||
| 5039 | |||
| 5040 | /** |
||
| 5041 | True when user credentials are to be included in a cross-origin request. False when they are to be excluded |
||
| 5042 | in a cross-origin request and when cookies are to be ignored in its response. Initially false. |
||
| 5043 | |||
| 5044 | @property withCredentials |
||
| 5045 | @type Boolean |
||
| 5046 | @default false |
||
| 5047 | */ |
||
| 5048 | withCredentials: false, |
||
| 5049 | |||
| 5050 | /** |
||
| 5051 | Returns the HTTP status code. |
||
| 5052 | |||
| 5053 | @property status |
||
| 5054 | @type Number |
||
| 5055 | @default 0 |
||
| 5056 | */ |
||
| 5057 | status: 0, |
||
| 5058 | |||
| 5059 | /** |
||
| 5060 | Returns the HTTP status text. |
||
| 5061 | |||
| 5062 | @property statusText |
||
| 5063 | @type String |
||
| 5064 | */ |
||
| 5065 | statusText: "", |
||
| 5066 | |||
| 5067 | /** |
||
| 5068 | Returns the response type. Can be set to change the response type. Values are: |
||
| 5069 | the empty string (default), "arraybuffer", "blob", "document", "json", and "text". |
||
| 5070 | |||
| 5071 | @property responseType |
||
| 5072 | @type String |
||
| 5073 | */ |
||
| 5074 | responseType: "", |
||
| 5075 | |||
| 5076 | /** |
||
| 5077 | Returns the document response entity body. |
||
| 5078 | |||
| 5079 | Throws an "InvalidStateError" exception if responseType is not the empty string or "document". |
||
| 5080 | |||
| 5081 | @property responseXML |
||
| 5082 | @type Document |
||
| 5083 | */ |
||
| 5084 | responseXML: null, |
||
| 5085 | |||
| 5086 | /** |
||
| 5087 | Returns the text response entity body. |
||
| 5088 | |||
| 5089 | Throws an "InvalidStateError" exception if responseType is not the empty string or "text". |
||
| 5090 | |||
| 5091 | @property responseText |
||
| 5092 | @type String |
||
| 5093 | */ |
||
| 5094 | responseText: null, |
||
| 5095 | |||
| 5096 | /** |
||
| 5097 | Returns the response entity body (http://www.w3.org/TR/XMLHttpRequest/#response-entity-body). |
||
| 5098 | Can become: ArrayBuffer, Blob, Document, JSON, Text |
||
| 5099 | |||
| 5100 | @property response |
||
| 5101 | @type Mixed |
||
| 5102 | */ |
||
| 5103 | response: null |
||
| 5104 | }, |
||
| 5105 | |||
| 5106 | _async = true, |
||
| 5107 | _url, |
||
| 5108 | _method, |
||
| 5109 | _headers = {}, |
||
| 5110 | _user, |
||
| 5111 | _password, |
||
| 5112 | _encoding = null, |
||
| 5113 | _mimeType = null, |
||
| 5114 | |||
| 5115 | // flags |
||
| 5116 | _sync_flag = false, |
||
| 5117 | _send_flag = false, |
||
| 5118 | _upload_events_flag = false, |
||
| 5119 | _upload_complete_flag = false, |
||
| 5120 | _error_flag = false, |
||
| 5121 | _same_origin_flag = false, |
||
| 5122 | |||
| 5123 | // times |
||
| 5124 | _start_time, |
||
| 5125 | _timeoutset_time, |
||
| 5126 | |||
| 5127 | _finalMime = null, |
||
| 5128 | _finalCharset = null, |
||
| 5129 | |||
| 5130 | _options = {}, |
||
| 5131 | _xhr, |
||
| 5132 | _responseHeaders = '', |
||
| 5133 | _responseHeadersBag |
||
| 5134 | ; |
||
| 5135 | |||
| 5136 | |||
| 5137 | Basic.extend(this, props, { |
||
| 5138 | /** |
||
| 5139 | Unique id of the component |
||
| 5140 | |||
| 5141 | @property uid |
||
| 5142 | @type String |
||
| 5143 | */ |
||
| 5144 | uid: Basic.guid('uid_'), |
||
| 5145 | |||
| 5146 | /** |
||
| 5147 | Target for Upload events |
||
| 5148 | |||
| 5149 | @property upload |
||
| 5150 | @type XMLHttpRequestUpload |
||
| 5151 | */ |
||
| 5152 | upload: new XMLHttpRequestUpload(), |
||
| 5153 | |||
| 5154 | |||
| 5155 | /** |
||
| 5156 | Sets the request method, request URL, synchronous flag, request username, and request password. |
||
| 5157 | |||
| 5158 | Throws a "SyntaxError" exception if one of the following is true: |
||
| 5159 | |||
| 5160 | method is not a valid HTTP method. |
||
| 5161 | url cannot be resolved. |
||
| 5162 | url contains the "user:password" format in the userinfo production. |
||
| 5163 | Throws a "SecurityError" exception if method is a case-insensitive match for CONNECT, TRACE or TRACK. |
||
| 5164 | |||
| 5165 | Throws an "InvalidAccessError" exception if one of the following is true: |
||
| 5166 | |||
| 5167 | Either user or password is passed as argument and the origin of url does not match the XMLHttpRequest origin. |
||
| 5168 | There is an associated XMLHttpRequest document and either the timeout attribute is not zero, |
||
| 5169 | the withCredentials attribute is true, or the responseType attribute is not the empty string. |
||
| 5170 | |||
| 5171 | |||
| 5172 | @method open |
||
| 5173 | @param {String} method HTTP method to use on request |
||
| 5174 | @param {String} url URL to request |
||
| 5175 | @param {Boolean} [async=true] If false request will be done in synchronous manner. Asynchronous by default. |
||
| 5176 | @param {String} [user] Username to use in HTTP authentication process on server-side |
||
| 5177 | @param {String} [password] Password to use in HTTP authentication process on server-side |
||
| 5178 | */ |
||
| 5179 | open: function(method, url, async, user, password) { |
||
| 5180 | var urlp; |
||
| 5181 | |||
| 5182 | // first two arguments are required |
||
| 5183 | if (!method || !url) { |
||
| 5184 | throw new x.DOMException(x.DOMException.SYNTAX_ERR); |
||
| 5185 | } |
||
| 5186 | |||
| 5187 | // 2 - check if any code point in method is higher than U+00FF or after deflating method it does not match the method |
||
| 5188 | if (/[\u0100-\uffff]/.test(method) || Encode.utf8_encode(method) !== method) { |
||
| 5189 | throw new x.DOMException(x.DOMException.SYNTAX_ERR); |
||
| 5190 | } |
||
| 5191 | |||
| 5192 | // 3 |
||
| 5193 | if (!!~Basic.inArray(method.toUpperCase(), ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'TRACE', 'TRACK'])) { |
||
| 5194 | _method = method.toUpperCase(); |
||
| 5195 | } |
||
| 5196 | |||
| 5197 | |||
| 5198 | // 4 - allowing these methods poses a security risk |
||
| 5199 | if (!!~Basic.inArray(_method, ['CONNECT', 'TRACE', 'TRACK'])) { |
||
| 5200 | throw new x.DOMException(x.DOMException.SECURITY_ERR); |
||
| 5201 | } |
||
| 5202 | |||
| 5203 | // 5 |
||
| 5204 | url = Encode.utf8_encode(url); |
||
| 5205 | |||
| 5206 | // 6 - Resolve url relative to the XMLHttpRequest base URL. If the algorithm returns an error, throw a "SyntaxError". |
||
| 5207 | urlp = Url.parseUrl(url); |
||
| 5208 | |||
| 5209 | _same_origin_flag = Url.hasSameOrigin(urlp); |
||
| 5210 | |||
| 5211 | // 7 - manually build up absolute url |
||
| 5212 | _url = Url.resolveUrl(url); |
||
| 5213 | |||
| 5214 | // 9-10, 12-13 |
||
| 5215 | if ((user || password) && !_same_origin_flag) { |
||
| 5216 | throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR); |
||
| 5217 | } |
||
| 5218 | |||
| 5219 | _user = user || urlp.user; |
||
| 5220 | _password = password || urlp.pass; |
||
| 5221 | |||
| 5222 | // 11 |
||
| 5223 | _async = async || true; |
||
| 5224 | |||
| 5225 | if (_async === false && (_p('timeout') || _p('withCredentials') || _p('responseType') !== "")) { |
||
| 5226 | throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR); |
||
| 5227 | } |
||
| 5228 | |||
| 5229 | // 14 - terminate abort() |
||
| 5230 | |||
| 5231 | // 15 - terminate send() |
||
| 5232 | |||
| 5233 | // 18 |
||
| 5234 | _sync_flag = !_async; |
||
| 5235 | _send_flag = false; |
||
| 5236 | _headers = {}; |
||
| 5237 | _reset.call(this); |
||
| 5238 | |||
| 5239 | // 19 |
||
| 5240 | _p('readyState', XMLHttpRequest.OPENED); |
||
| 5241 | |||
| 5242 | // 20 |
||
| 5243 | this.dispatchEvent('readystatechange'); |
||
| 5244 | }, |
||
| 5245 | |||
| 5246 | /** |
||
| 5247 | Appends an header to the list of author request headers, or if header is already |
||
| 5248 | in the list of author request headers, combines its value with value. |
||
| 5249 | |||
| 5250 | Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set. |
||
| 5251 | Throws a "SyntaxError" exception if header is not a valid HTTP header field name or if value |
||
| 5252 | is not a valid HTTP header field value. |
||
| 5253 | |||
| 5254 | @method setRequestHeader |
||
| 5255 | @param {String} header |
||
| 5256 | @param {String|Number} value |
||
| 5257 | */ |
||
| 5258 | setRequestHeader: function(header, value) { |
||
| 5259 | var uaHeaders = [ // these headers are controlled by the user agent |
||
| 5260 | "accept-charset", |
||
| 5261 | "accept-encoding", |
||
| 5262 | "access-control-request-headers", |
||
| 5263 | "access-control-request-method", |
||
| 5264 | "connection", |
||
| 5265 | "content-length", |
||
| 5266 | "cookie", |
||
| 5267 | "cookie2", |
||
| 5268 | "content-transfer-encoding", |
||
| 5269 | "date", |
||
| 5270 | "expect", |
||
| 5271 | "host", |
||
| 5272 | "keep-alive", |
||
| 5273 | "origin", |
||
| 5274 | "referer", |
||
| 5275 | "te", |
||
| 5276 | "trailer", |
||
| 5277 | "transfer-encoding", |
||
| 5278 | "upgrade", |
||
| 5279 | "user-agent", |
||
| 5280 | "via" |
||
| 5281 | ]; |
||
| 5282 | |||
| 5283 | // 1-2 |
||
| 5284 | if (_p('readyState') !== XMLHttpRequest.OPENED || _send_flag) { |
||
| 5285 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5286 | } |
||
| 5287 | |||
| 5288 | // 3 |
||
| 5289 | if (/[\u0100-\uffff]/.test(header) || Encode.utf8_encode(header) !== header) { |
||
| 5290 | throw new x.DOMException(x.DOMException.SYNTAX_ERR); |
||
| 5291 | } |
||
| 5292 | |||
| 5293 | // 4 |
||
| 5294 | /* this step is seemingly bypassed in browsers, probably to allow various unicode characters in header values |
||
| 5295 | if (/[\u0100-\uffff]/.test(value) || Encode.utf8_encode(value) !== value) { |
||
| 5296 | throw new x.DOMException(x.DOMException.SYNTAX_ERR); |
||
| 5297 | }*/ |
||
| 5298 | |||
| 5299 | header = Basic.trim(header).toLowerCase(); |
||
| 5300 | |||
| 5301 | // setting of proxy-* and sec-* headers is prohibited by spec |
||
| 5302 | if (!!~Basic.inArray(header, uaHeaders) || /^(proxy\-|sec\-)/.test(header)) { |
||
| 5303 | return false; |
||
| 5304 | } |
||
| 5305 | |||
| 5306 | // camelize |
||
| 5307 | // browsers lowercase header names (at least for custom ones) |
||
| 5308 | // header = header.replace(/\b\w/g, function($1) { return $1.toUpperCase(); }); |
||
| 5309 | |||
| 5310 | if (!_headers[header]) { |
||
| 5311 | _headers[header] = value; |
||
| 5312 | } else { |
||
| 5313 | // http://tools.ietf.org/html/rfc2616#section-4.2 (last paragraph) |
||
| 5314 | _headers[header] += ', ' + value; |
||
| 5315 | } |
||
| 5316 | return true; |
||
| 5317 | }, |
||
| 5318 | |||
| 5319 | /** |
||
| 5320 | * Test if the specified header is already set on this request. |
||
| 5321 | * Returns a header value or boolean false if it's not yet set. |
||
| 5322 | * |
||
| 5323 | * @method hasRequestHeader |
||
| 5324 | * @param {String} header Name of the header to test |
||
| 5325 | * @return {Boolean|String} |
||
| 5326 | */ |
||
| 5327 | hasRequestHeader: function(header) { |
||
| 5328 | return header && _headers[header.toLowerCase()] || false; |
||
| 5329 | }, |
||
| 5330 | |||
| 5331 | /** |
||
| 5332 | Returns all headers from the response, with the exception of those whose field name is Set-Cookie or Set-Cookie2. |
||
| 5333 | |||
| 5334 | @method getAllResponseHeaders |
||
| 5335 | @return {String} reponse headers or empty string |
||
| 5336 | */ |
||
| 5337 | getAllResponseHeaders: function() { |
||
| 5338 | return _responseHeaders || ''; |
||
| 5339 | }, |
||
| 5340 | |||
| 5341 | /** |
||
| 5342 | Returns the header field value from the response of which the field name matches header, |
||
| 5343 | unless the field name is Set-Cookie or Set-Cookie2. |
||
| 5344 | |||
| 5345 | @method getResponseHeader |
||
| 5346 | @param {String} header |
||
| 5347 | @return {String} value(s) for the specified header or null |
||
| 5348 | */ |
||
| 5349 | getResponseHeader: function(header) { |
||
| 5350 | header = header.toLowerCase(); |
||
| 5351 | |||
| 5352 | if (_error_flag || !!~Basic.inArray(header, ['set-cookie', 'set-cookie2'])) { |
||
| 5353 | return null; |
||
| 5354 | } |
||
| 5355 | |||
| 5356 | if (_responseHeaders && _responseHeaders !== '') { |
||
| 5357 | // if we didn't parse response headers until now, do it and keep for later |
||
| 5358 | if (!_responseHeadersBag) { |
||
| 5359 | _responseHeadersBag = {}; |
||
| 5360 | Basic.each(_responseHeaders.split(/\r\n/), function(line) { |
||
| 5361 | var pair = line.split(/:\s+/); |
||
| 5362 | if (pair.length === 2) { // last line might be empty, omit |
||
| 5363 | pair[0] = Basic.trim(pair[0]); // just in case |
||
| 5364 | _responseHeadersBag[pair[0].toLowerCase()] = { // simply to retain header name in original form |
||
| 5365 | header: pair[0], |
||
| 5366 | value: Basic.trim(pair[1]) |
||
| 5367 | }; |
||
| 5368 | } |
||
| 5369 | }); |
||
| 5370 | } |
||
| 5371 | if (_responseHeadersBag.hasOwnProperty(header)) { |
||
| 5372 | return _responseHeadersBag[header].header + ': ' + _responseHeadersBag[header].value; |
||
| 5373 | } |
||
| 5374 | } |
||
| 5375 | return null; |
||
| 5376 | }, |
||
| 5377 | |||
| 5378 | /** |
||
| 5379 | Sets the Content-Type header for the response to mime. |
||
| 5380 | Throws an "InvalidStateError" exception if the state is LOADING or DONE. |
||
| 5381 | Throws a "SyntaxError" exception if mime is not a valid media type. |
||
| 5382 | |||
| 5383 | @method overrideMimeType |
||
| 5384 | @param String mime Mime type to set |
||
| 5385 | */ |
||
| 5386 | overrideMimeType: function(mime) { |
||
| 5387 | var matches, charset; |
||
| 5388 | |||
| 5389 | // 1 |
||
| 5390 | if (!!~Basic.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) { |
||
| 5391 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5392 | } |
||
| 5393 | |||
| 5394 | // 2 |
||
| 5395 | mime = Basic.trim(mime.toLowerCase()); |
||
| 5396 | |||
| 5397 | if (/;/.test(mime) && (matches = mime.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))) { |
||
| 5398 | mime = matches[1]; |
||
| 5399 | if (matches[2]) { |
||
| 5400 | charset = matches[2]; |
||
| 5401 | } |
||
| 5402 | } |
||
| 5403 | |||
| 5404 | if (!Mime.mimes[mime]) { |
||
| 5405 | throw new x.DOMException(x.DOMException.SYNTAX_ERR); |
||
| 5406 | } |
||
| 5407 | |||
| 5408 | // 3-4 |
||
| 5409 | _finalMime = mime; |
||
| 5410 | _finalCharset = charset; |
||
| 5411 | }, |
||
| 5412 | |||
| 5413 | /** |
||
| 5414 | Initiates the request. The optional argument provides the request entity body. |
||
| 5415 | The argument is ignored if request method is GET or HEAD. |
||
| 5416 | |||
| 5417 | Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set. |
||
| 5418 | |||
| 5419 | @method send |
||
| 5420 | @param {Blob|Document|String|FormData} [data] Request entity body |
||
| 5421 | @param {Object} [options] Set of requirements and pre-requisities for runtime initialization |
||
| 5422 | */ |
||
| 5423 | send: function(data, options) { |
||
| 5424 | if (Basic.typeOf(options) === 'string') { |
||
| 5425 | _options = { ruid: options }; |
||
| 5426 | } else if (!options) { |
||
| 5427 | _options = {}; |
||
| 5428 | } else { |
||
| 5429 | _options = options; |
||
| 5430 | } |
||
| 5431 | |||
| 5432 | // 1-2 |
||
| 5433 | if (this.readyState !== XMLHttpRequest.OPENED || _send_flag) { |
||
| 5434 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5435 | } |
||
| 5436 | |||
| 5437 | // 3 |
||
| 5438 | // sending Blob |
||
| 5439 | if (data instanceof Blob) { |
||
| 5440 | _options.ruid = data.ruid; |
||
| 5441 | _mimeType = data.type || 'application/octet-stream'; |
||
| 5442 | } |
||
| 5443 | |||
| 5444 | // FormData |
||
| 5445 | else if (data instanceof FormData) { |
||
| 5446 | if (data.hasBlob()) { |
||
| 5447 | var blob = data.getBlob(); |
||
| 5448 | _options.ruid = blob.ruid; |
||
| 5449 | _mimeType = blob.type || 'application/octet-stream'; |
||
| 5450 | } |
||
| 5451 | } |
||
| 5452 | |||
| 5453 | // DOMString |
||
| 5454 | else if (typeof data === 'string') { |
||
| 5455 | _encoding = 'UTF-8'; |
||
| 5456 | _mimeType = 'text/plain;charset=UTF-8'; |
||
| 5457 | |||
| 5458 | // data should be converted to Unicode and encoded as UTF-8 |
||
| 5459 | data = Encode.utf8_encode(data); |
||
| 5460 | } |
||
| 5461 | |||
| 5462 | // if withCredentials not set, but requested, set it automatically |
||
| 5463 | if (!this.withCredentials) { |
||
| 5464 | this.withCredentials = (_options.required_caps && _options.required_caps.send_browser_cookies) && !_same_origin_flag; |
||
| 5465 | } |
||
| 5466 | |||
| 5467 | // 4 - storage mutex |
||
| 5468 | // 5 |
||
| 5469 | _upload_events_flag = (!_sync_flag && this.upload.hasEventListener()); // DSAP |
||
| 5470 | // 6 |
||
| 5471 | _error_flag = false; |
||
| 5472 | // 7 |
||
| 5473 | _upload_complete_flag = !data; |
||
| 5474 | // 8 - Asynchronous steps |
||
| 5475 | if (!_sync_flag) { |
||
| 5476 | // 8.1 |
||
| 5477 | _send_flag = true; |
||
| 5478 | // 8.2 |
||
| 5479 | // this.dispatchEvent('loadstart'); // will be dispatched either by native or runtime xhr |
||
| 5480 | // 8.3 |
||
| 5481 | //if (!_upload_complete_flag) { |
||
| 5482 | // this.upload.dispatchEvent('loadstart'); // will be dispatched either by native or runtime xhr |
||
| 5483 | //} |
||
| 5484 | } |
||
| 5485 | // 8.5 - Return the send() method call, but continue running the steps in this algorithm. |
||
| 5486 | _doXHR.call(this, data); |
||
| 5487 | }, |
||
| 5488 | |||
| 5489 | /** |
||
| 5490 | Cancels any network activity. |
||
| 5491 | |||
| 5492 | @method abort |
||
| 5493 | */ |
||
| 5494 | abort: function() { |
||
| 5495 | _error_flag = true; |
||
| 5496 | _sync_flag = false; |
||
| 5497 | |||
| 5498 | if (!~Basic.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED, XMLHttpRequest.DONE])) { |
||
| 5499 | _p('readyState', XMLHttpRequest.DONE); |
||
| 5500 | _send_flag = false; |
||
| 5501 | |||
| 5502 | if (_xhr) { |
||
| 5503 | _xhr.getRuntime().exec.call(_xhr, 'XMLHttpRequest', 'abort', _upload_complete_flag); |
||
| 5504 | } else { |
||
| 5505 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5506 | } |
||
| 5507 | |||
| 5508 | _upload_complete_flag = true; |
||
| 5509 | } else { |
||
| 5510 | _p('readyState', XMLHttpRequest.UNSENT); |
||
| 5511 | } |
||
| 5512 | }, |
||
| 5513 | |||
| 5514 | destroy: function() { |
||
| 5515 | if (_xhr) { |
||
| 5516 | if (Basic.typeOf(_xhr.destroy) === 'function') { |
||
| 5517 | _xhr.destroy(); |
||
| 5518 | } |
||
| 5519 | _xhr = null; |
||
| 5520 | } |
||
| 5521 | |||
| 5522 | this.unbindAll(); |
||
| 5523 | |||
| 5524 | if (this.upload) { |
||
| 5525 | this.upload.unbindAll(); |
||
| 5526 | this.upload = null; |
||
| 5527 | } |
||
| 5528 | } |
||
| 5529 | }); |
||
| 5530 | |||
| 5531 | this.handleEventProps(dispatches.concat(['readystatechange'])); // for historical reasons |
||
| 5532 | this.upload.handleEventProps(dispatches); |
||
| 5533 | |||
| 5534 | /* this is nice, but maybe too lengthy |
||
| 5535 | |||
| 5536 | // if supported by JS version, set getters/setters for specific properties |
||
| 5537 | o.defineProperty(this, 'readyState', { |
||
| 5538 | configurable: false, |
||
| 5539 | |||
| 5540 | get: function() { |
||
| 5541 | return _p('readyState'); |
||
| 5542 | } |
||
| 5543 | }); |
||
| 5544 | |||
| 5545 | o.defineProperty(this, 'timeout', { |
||
| 5546 | configurable: false, |
||
| 5547 | |||
| 5548 | get: function() { |
||
| 5549 | return _p('timeout'); |
||
| 5550 | }, |
||
| 5551 | |||
| 5552 | set: function(value) { |
||
| 5553 | |||
| 5554 | if (_sync_flag) { |
||
| 5555 | throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR); |
||
| 5556 | } |
||
| 5557 | |||
| 5558 | // timeout still should be measured relative to the start time of request |
||
| 5559 | _timeoutset_time = (new Date).getTime(); |
||
| 5560 | |||
| 5561 | _p('timeout', value); |
||
| 5562 | } |
||
| 5563 | }); |
||
| 5564 | |||
| 5565 | // the withCredentials attribute has no effect when fetching same-origin resources |
||
| 5566 | o.defineProperty(this, 'withCredentials', { |
||
| 5567 | configurable: false, |
||
| 5568 | |||
| 5569 | get: function() { |
||
| 5570 | return _p('withCredentials'); |
||
| 5571 | }, |
||
| 5572 | |||
| 5573 | set: function(value) { |
||
| 5574 | // 1-2 |
||
| 5575 | if (!~o.inArray(_p('readyState'), [XMLHttpRequest.UNSENT, XMLHttpRequest.OPENED]) || _send_flag) { |
||
| 5576 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5577 | } |
||
| 5578 | |||
| 5579 | // 3-4 |
||
| 5580 | if (_anonymous_flag || _sync_flag) { |
||
| 5581 | throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR); |
||
| 5582 | } |
||
| 5583 | |||
| 5584 | // 5 |
||
| 5585 | _p('withCredentials', value); |
||
| 5586 | } |
||
| 5587 | }); |
||
| 5588 | |||
| 5589 | o.defineProperty(this, 'status', { |
||
| 5590 | configurable: false, |
||
| 5591 | |||
| 5592 | get: function() { |
||
| 5593 | return _p('status'); |
||
| 5594 | } |
||
| 5595 | }); |
||
| 5596 | |||
| 5597 | o.defineProperty(this, 'statusText', { |
||
| 5598 | configurable: false, |
||
| 5599 | |||
| 5600 | get: function() { |
||
| 5601 | return _p('statusText'); |
||
| 5602 | } |
||
| 5603 | }); |
||
| 5604 | |||
| 5605 | o.defineProperty(this, 'responseType', { |
||
| 5606 | configurable: false, |
||
| 5607 | |||
| 5608 | get: function() { |
||
| 5609 | return _p('responseType'); |
||
| 5610 | }, |
||
| 5611 | |||
| 5612 | set: function(value) { |
||
| 5613 | // 1 |
||
| 5614 | if (!!~o.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) { |
||
| 5615 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5616 | } |
||
| 5617 | |||
| 5618 | // 2 |
||
| 5619 | if (_sync_flag) { |
||
| 5620 | throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR); |
||
| 5621 | } |
||
| 5622 | |||
| 5623 | // 3 |
||
| 5624 | _p('responseType', value.toLowerCase()); |
||
| 5625 | } |
||
| 5626 | }); |
||
| 5627 | |||
| 5628 | o.defineProperty(this, 'responseText', { |
||
| 5629 | configurable: false, |
||
| 5630 | |||
| 5631 | get: function() { |
||
| 5632 | // 1 |
||
| 5633 | if (!~o.inArray(_p('responseType'), ['', 'text'])) { |
||
| 5634 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5635 | } |
||
| 5636 | |||
| 5637 | // 2-3 |
||
| 5638 | if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) { |
||
| 5639 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5640 | } |
||
| 5641 | |||
| 5642 | return _p('responseText'); |
||
| 5643 | } |
||
| 5644 | }); |
||
| 5645 | |||
| 5646 | o.defineProperty(this, 'responseXML', { |
||
| 5647 | configurable: false, |
||
| 5648 | |||
| 5649 | get: function() { |
||
| 5650 | // 1 |
||
| 5651 | if (!~o.inArray(_p('responseType'), ['', 'document'])) { |
||
| 5652 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5653 | } |
||
| 5654 | |||
| 5655 | // 2-3 |
||
| 5656 | if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) { |
||
| 5657 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 5658 | } |
||
| 5659 | |||
| 5660 | return _p('responseXML'); |
||
| 5661 | } |
||
| 5662 | }); |
||
| 5663 | |||
| 5664 | o.defineProperty(this, 'response', { |
||
| 5665 | configurable: false, |
||
| 5666 | |||
| 5667 | get: function() { |
||
| 5668 | if (!!~o.inArray(_p('responseType'), ['', 'text'])) { |
||
| 5669 | if (_p('readyState') !== XMLHttpRequest.DONE && _p('readyState') !== XMLHttpRequest.LOADING || _error_flag) { |
||
| 5670 | return ''; |
||
| 5671 | } |
||
| 5672 | } |
||
| 5673 | |||
| 5674 | if (_p('readyState') !== XMLHttpRequest.DONE || _error_flag) { |
||
| 5675 | return null; |
||
| 5676 | } |
||
| 5677 | |||
| 5678 | return _p('response'); |
||
| 5679 | } |
||
| 5680 | }); |
||
| 5681 | |||
| 5682 | */ |
||
| 5683 | |||
| 5684 | function _p(prop, value) { |
||
| 5685 | if (!props.hasOwnProperty(prop)) { |
||
| 5686 | return; |
||
| 5687 | } |
||
| 5688 | if (arguments.length === 1) { // get |
||
| 5689 | return Env.can('define_property') ? props[prop] : self[prop]; |
||
| 5690 | } else { // set |
||
| 5691 | if (Env.can('define_property')) { |
||
| 5692 | props[prop] = value; |
||
| 5693 | } else { |
||
| 5694 | self[prop] = value; |
||
| 5695 | } |
||
| 5696 | } |
||
| 5697 | } |
||
| 5698 | |||
| 5699 | /* |
||
| 5700 | function _toASCII(str, AllowUnassigned, UseSTD3ASCIIRules) { |
||
| 5701 | // TODO: http://tools.ietf.org/html/rfc3490#section-4.1 |
||
| 5702 | return str.toLowerCase(); |
||
| 5703 | } |
||
| 5704 | */ |
||
| 5705 | |||
| 5706 | |||
| 5707 | function _doXHR(data) { |
||
| 5708 | var self = this; |
||
| 5709 | |||
| 5710 | _start_time = new Date().getTime(); |
||
| 5711 | |||
| 5712 | _xhr = new RuntimeTarget(); |
||
| 5713 | |||
| 5714 | function loadEnd() { |
||
| 5715 | if (_xhr) { // it could have been destroyed by now |
||
| 5716 | _xhr.destroy(); |
||
| 5717 | _xhr = null; |
||
| 5718 | } |
||
| 5719 | self.dispatchEvent('loadend'); |
||
| 5720 | self = null; |
||
| 5721 | } |
||
| 5722 | |||
| 5723 | function exec(runtime) { |
||
| 5724 | _xhr.bind('LoadStart', function(e) { |
||
| 5725 | _p('readyState', XMLHttpRequest.LOADING); |
||
| 5726 | self.dispatchEvent('readystatechange'); |
||
| 5727 | |||
| 5728 | self.dispatchEvent(e); |
||
| 5729 | |||
| 5730 | if (_upload_events_flag) { |
||
| 5731 | self.upload.dispatchEvent(e); |
||
| 5732 | } |
||
| 5733 | }); |
||
| 5734 | |||
| 5735 | _xhr.bind('Progress', function(e) { |
||
| 5736 | if (_p('readyState') !== XMLHttpRequest.LOADING) { |
||
| 5737 | _p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example) |
||
| 5738 | self.dispatchEvent('readystatechange'); |
||
| 5739 | } |
||
| 5740 | self.dispatchEvent(e); |
||
| 5741 | }); |
||
| 5742 | |||
| 5743 | _xhr.bind('UploadProgress', function(e) { |
||
| 5744 | if (_upload_events_flag) { |
||
| 5745 | self.upload.dispatchEvent({ |
||
| 5746 | type: 'progress', |
||
| 5747 | lengthComputable: false, |
||
| 5748 | total: e.total, |
||
| 5749 | loaded: e.loaded |
||
| 5750 | }); |
||
| 5751 | } |
||
| 5752 | }); |
||
| 5753 | |||
| 5754 | _xhr.bind('Load', function(e) { |
||
| 5755 | _p('readyState', XMLHttpRequest.DONE); |
||
| 5756 | _p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0)); |
||
| 5757 | _p('statusText', httpCode[_p('status')] || ""); |
||
| 5758 | |||
| 5759 | _p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType'))); |
||
| 5760 | |||
| 5761 | if (!!~Basic.inArray(_p('responseType'), ['text', ''])) { |
||
| 5762 | _p('responseText', _p('response')); |
||
| 5763 | } else if (_p('responseType') === 'document') { |
||
| 5764 | _p('responseXML', _p('response')); |
||
| 5765 | } |
||
| 5766 | |||
| 5767 | _responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders'); |
||
| 5768 | |||
| 5769 | self.dispatchEvent('readystatechange'); |
||
| 5770 | |||
| 5771 | if (_p('status') > 0) { // status 0 usually means that server is unreachable |
||
| 5772 | if (_upload_events_flag) { |
||
| 5773 | self.upload.dispatchEvent(e); |
||
| 5774 | } |
||
| 5775 | self.dispatchEvent(e); |
||
| 5776 | } else { |
||
| 5777 | _error_flag = true; |
||
| 5778 | self.dispatchEvent('error'); |
||
| 5779 | } |
||
| 5780 | loadEnd(); |
||
| 5781 | }); |
||
| 5782 | |||
| 5783 | _xhr.bind('Abort', function(e) { |
||
| 5784 | self.dispatchEvent(e); |
||
| 5785 | loadEnd(); |
||
| 5786 | }); |
||
| 5787 | |||
| 5788 | _xhr.bind('Error', function(e) { |
||
| 5789 | _error_flag = true; |
||
| 5790 | _p('readyState', XMLHttpRequest.DONE); |
||
| 5791 | self.dispatchEvent('readystatechange'); |
||
| 5792 | _upload_complete_flag = true; |
||
| 5793 | self.dispatchEvent(e); |
||
| 5794 | loadEnd(); |
||
| 5795 | }); |
||
| 5796 | |||
| 5797 | runtime.exec.call(_xhr, 'XMLHttpRequest', 'send', { |
||
| 5798 | url: _url, |
||
| 5799 | method: _method, |
||
| 5800 | async: _async, |
||
| 5801 | user: _user, |
||
| 5802 | password: _password, |
||
| 5803 | headers: _headers, |
||
| 5804 | mimeType: _mimeType, |
||
| 5805 | encoding: _encoding, |
||
| 5806 | responseType: self.responseType, |
||
| 5807 | withCredentials: self.withCredentials, |
||
| 5808 | options: _options |
||
| 5809 | }, data); |
||
| 5810 | } |
||
| 5811 | |||
| 5812 | // clarify our requirements |
||
| 5813 | if (typeof(_options.required_caps) === 'string') { |
||
| 5814 | _options.required_caps = Runtime.parseCaps(_options.required_caps); |
||
| 5815 | } |
||
| 5816 | |||
| 5817 | _options.required_caps = Basic.extend({}, _options.required_caps, { |
||
| 5818 | return_response_type: self.responseType |
||
| 5819 | }); |
||
| 5820 | |||
| 5821 | if (data instanceof FormData) { |
||
| 5822 | _options.required_caps.send_multipart = true; |
||
| 5823 | } |
||
| 5824 | |||
| 5825 | if (!Basic.isEmptyObj(_headers)) { |
||
| 5826 | _options.required_caps.send_custom_headers = true; |
||
| 5827 | } |
||
| 5828 | |||
| 5829 | if (!_same_origin_flag) { |
||
| 5830 | _options.required_caps.do_cors = true; |
||
| 5831 | } |
||
| 5832 | |||
| 5833 | |||
| 5834 | if (_options.ruid) { // we do not need to wait if we can connect directly |
||
| 5835 | exec(_xhr.connectRuntime(_options)); |
||
| 5836 | } else { |
||
| 5837 | _xhr.bind('RuntimeInit', function(e, runtime) { |
||
| 5838 | exec(runtime); |
||
| 5839 | }); |
||
| 5840 | _xhr.bind('RuntimeError', function(e, err) { |
||
| 5841 | self.dispatchEvent('RuntimeError', err); |
||
| 5842 | }); |
||
| 5843 | _xhr.connectRuntime(_options); |
||
| 5844 | } |
||
| 5845 | } |
||
| 5846 | |||
| 5847 | |||
| 5848 | function _reset() { |
||
| 5849 | _p('responseText', ""); |
||
| 5850 | _p('responseXML', null); |
||
| 5851 | _p('response', null); |
||
| 5852 | _p('status', 0); |
||
| 5853 | _p('statusText', ""); |
||
| 5854 | _start_time = _timeoutset_time = null; |
||
| 5855 | } |
||
| 5856 | } |
||
| 5857 | |||
| 5858 | XMLHttpRequest.UNSENT = 0; |
||
| 5859 | XMLHttpRequest.OPENED = 1; |
||
| 5860 | XMLHttpRequest.HEADERS_RECEIVED = 2; |
||
| 5861 | XMLHttpRequest.LOADING = 3; |
||
| 5862 | XMLHttpRequest.DONE = 4; |
||
| 5863 | |||
| 5864 | XMLHttpRequest.prototype = EventTarget.instance; |
||
| 5865 | |||
| 5866 | return XMLHttpRequest; |
||
| 5867 | }); |
||
| 5868 | |||
| 5869 | // Included from: src/javascript/runtime/Transporter.js |
||
| 5870 | |||
| 5871 | /** |
||
| 5872 | * Transporter.js |
||
| 5873 | * |
||
| 5874 | * Copyright 2013, Moxiecode Systems AB |
||
| 5875 | * Released under GPL License. |
||
| 5876 | * |
||
| 5877 | * License: http://www.plupload.com/license |
||
| 5878 | * Contributing: http://www.plupload.com/contributing |
||
| 5879 | */ |
||
| 5880 | |||
| 5881 | define("moxie/runtime/Transporter", [ |
||
| 5882 | "moxie/core/utils/Basic", |
||
| 5883 | "moxie/core/utils/Encode", |
||
| 5884 | "moxie/runtime/RuntimeClient", |
||
| 5885 | "moxie/core/EventTarget" |
||
| 5886 | ], function(Basic, Encode, RuntimeClient, EventTarget) { |
||
| 5887 | |||
| 5888 | /** |
||
| 5889 | @class moxie/runtime/Transporter |
||
| 5890 | @private |
||
| 5891 | @constructor |
||
| 5892 | */ |
||
| 5893 | function Transporter() { |
||
| 5894 | var mod, _runtime, _data, _size, _pos, _chunk_size; |
||
| 5895 | |||
| 5896 | RuntimeClient.call(this); |
||
| 5897 | |||
| 5898 | Basic.extend(this, { |
||
| 5899 | uid: Basic.guid('uid_'), |
||
| 5900 | |||
| 5901 | state: Transporter.IDLE, |
||
| 5902 | |||
| 5903 | result: null, |
||
| 5904 | |||
| 5905 | transport: function(data, type, options) { |
||
| 5906 | var self = this; |
||
| 5907 | |||
| 5908 | options = Basic.extend({ |
||
| 5909 | chunk_size: 204798 |
||
| 5910 | }, options); |
||
| 5911 | |||
| 5912 | // should divide by three, base64 requires this |
||
| 5913 | if ((mod = options.chunk_size % 3)) { |
||
| 5914 | options.chunk_size += 3 - mod; |
||
| 5915 | } |
||
| 5916 | |||
| 5917 | _chunk_size = options.chunk_size; |
||
| 5918 | |||
| 5919 | _reset.call(this); |
||
| 5920 | _data = data; |
||
| 5921 | _size = data.length; |
||
| 5922 | |||
| 5923 | if (Basic.typeOf(options) === 'string' || options.ruid) { |
||
| 5924 | _run.call(self, type, this.connectRuntime(options)); |
||
| 5925 | } else { |
||
| 5926 | // we require this to run only once |
||
| 5927 | var cb = function(e, runtime) { |
||
| 5928 | self.unbind("RuntimeInit", cb); |
||
| 5929 | _run.call(self, type, runtime); |
||
| 5930 | }; |
||
| 5931 | this.bind("RuntimeInit", cb); |
||
| 5932 | this.connectRuntime(options); |
||
| 5933 | } |
||
| 5934 | }, |
||
| 5935 | |||
| 5936 | abort: function() { |
||
| 5937 | var self = this; |
||
| 5938 | |||
| 5939 | self.state = Transporter.IDLE; |
||
| 5940 | if (_runtime) { |
||
| 5941 | _runtime.exec.call(self, 'Transporter', 'clear'); |
||
| 5942 | self.trigger("TransportingAborted"); |
||
| 5943 | } |
||
| 5944 | |||
| 5945 | _reset.call(self); |
||
| 5946 | }, |
||
| 5947 | |||
| 5948 | |||
| 5949 | destroy: function() { |
||
| 5950 | this.unbindAll(); |
||
| 5951 | _runtime = null; |
||
| 5952 | this.disconnectRuntime(); |
||
| 5953 | _reset.call(this); |
||
| 5954 | } |
||
| 5955 | }); |
||
| 5956 | |||
| 5957 | function _reset() { |
||
| 5958 | _size = _pos = 0; |
||
| 5959 | _data = this.result = null; |
||
| 5960 | } |
||
| 5961 | |||
| 5962 | function _run(type, runtime) { |
||
| 5963 | var self = this; |
||
| 5964 | |||
| 5965 | _runtime = runtime; |
||
| 5966 | |||
| 5967 | //self.unbind("RuntimeInit"); |
||
| 5968 | |||
| 5969 | self.bind("TransportingProgress", function(e) { |
||
| 5970 | _pos = e.loaded; |
||
| 5971 | |||
| 5972 | if (_pos < _size && Basic.inArray(self.state, [Transporter.IDLE, Transporter.DONE]) === -1) { |
||
| 5973 | _transport.call(self); |
||
| 5974 | } |
||
| 5975 | }, 999); |
||
| 5976 | |||
| 5977 | self.bind("TransportingComplete", function() { |
||
| 5978 | _pos = _size; |
||
| 5979 | self.state = Transporter.DONE; |
||
| 5980 | _data = null; // clean a bit |
||
| 5981 | self.result = _runtime.exec.call(self, 'Transporter', 'getAsBlob', type || ''); |
||
| 5982 | }, 999); |
||
| 5983 | |||
| 5984 | self.state = Transporter.BUSY; |
||
| 5985 | self.trigger("TransportingStarted"); |
||
| 5986 | _transport.call(self); |
||
| 5987 | } |
||
| 5988 | |||
| 5989 | function _transport() { |
||
| 5990 | var self = this, |
||
| 5991 | chunk, |
||
| 5992 | bytesLeft = _size - _pos; |
||
| 5993 | |||
| 5994 | if (_chunk_size > bytesLeft) { |
||
| 5995 | _chunk_size = bytesLeft; |
||
| 5996 | } |
||
| 5997 | |||
| 5998 | chunk = Encode.btoa(_data.substr(_pos, _chunk_size)); |
||
| 5999 | _runtime.exec.call(self, 'Transporter', 'receive', chunk, _size); |
||
| 6000 | } |
||
| 6001 | } |
||
| 6002 | |||
| 6003 | Transporter.IDLE = 0; |
||
| 6004 | Transporter.BUSY = 1; |
||
| 6005 | Transporter.DONE = 2; |
||
| 6006 | |||
| 6007 | Transporter.prototype = EventTarget.instance; |
||
| 6008 | |||
| 6009 | return Transporter; |
||
| 6010 | }); |
||
| 6011 | |||
| 6012 | // Included from: src/javascript/image/Image.js |
||
| 6013 | |||
| 6014 | /** |
||
| 6015 | * Image.js |
||
| 6016 | * |
||
| 6017 | * Copyright 2013, Moxiecode Systems AB |
||
| 6018 | * Released under GPL License. |
||
| 6019 | * |
||
| 6020 | * License: http://www.plupload.com/license |
||
| 6021 | * Contributing: http://www.plupload.com/contributing |
||
| 6022 | */ |
||
| 6023 | |||
| 6024 | define("moxie/image/Image", [ |
||
| 6025 | "moxie/core/utils/Basic", |
||
| 6026 | "moxie/core/utils/Dom", |
||
| 6027 | "moxie/core/Exceptions", |
||
| 6028 | "moxie/file/FileReaderSync", |
||
| 6029 | "moxie/xhr/XMLHttpRequest", |
||
| 6030 | "moxie/runtime/Runtime", |
||
| 6031 | "moxie/runtime/RuntimeClient", |
||
| 6032 | "moxie/runtime/Transporter", |
||
| 6033 | "moxie/core/utils/Env", |
||
| 6034 | "moxie/core/EventTarget", |
||
| 6035 | "moxie/file/Blob", |
||
| 6036 | "moxie/file/File", |
||
| 6037 | "moxie/core/utils/Encode" |
||
| 6038 | ], function(Basic, Dom, x, FileReaderSync, XMLHttpRequest, Runtime, RuntimeClient, Transporter, Env, EventTarget, Blob, File, Encode) { |
||
| 6039 | /** |
||
| 6040 | Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data. |
||
| 6041 | |||
| 6042 | @class moxie/image/Image |
||
| 6043 | @constructor |
||
| 6044 | @extends EventTarget |
||
| 6045 | */ |
||
| 6046 | var dispatches = [ |
||
| 6047 | 'progress', |
||
| 6048 | |||
| 6049 | /** |
||
| 6050 | Dispatched when loading is complete. |
||
| 6051 | |||
| 6052 | @event load |
||
| 6053 | @param {Object} event |
||
| 6054 | */ |
||
| 6055 | 'load', |
||
| 6056 | |||
| 6057 | 'error', |
||
| 6058 | |||
| 6059 | /** |
||
| 6060 | Dispatched when resize operation is complete. |
||
| 6061 | |||
| 6062 | @event resize |
||
| 6063 | @param {Object} event |
||
| 6064 | */ |
||
| 6065 | 'resize', |
||
| 6066 | |||
| 6067 | /** |
||
| 6068 | Dispatched when visual representation of the image is successfully embedded |
||
| 6069 | into the corresponsing container. |
||
| 6070 | |||
| 6071 | @event embedded |
||
| 6072 | @param {Object} event |
||
| 6073 | */ |
||
| 6074 | 'embedded' |
||
| 6075 | ]; |
||
| 6076 | |||
| 6077 | function Image() { |
||
| 6078 | |||
| 6079 | RuntimeClient.call(this); |
||
| 6080 | |||
| 6081 | Basic.extend(this, { |
||
| 6082 | /** |
||
| 6083 | Unique id of the component |
||
| 6084 | |||
| 6085 | @property uid |
||
| 6086 | @type {String} |
||
| 6087 | */ |
||
| 6088 | uid: Basic.guid('uid_'), |
||
| 6089 | |||
| 6090 | /** |
||
| 6091 | Unique id of the connected runtime, if any. |
||
| 6092 | |||
| 6093 | @property ruid |
||
| 6094 | @type {String} |
||
| 6095 | */ |
||
| 6096 | ruid: null, |
||
| 6097 | |||
| 6098 | /** |
||
| 6099 | Name of the file, that was used to create an image, if available. If not equals to empty string. |
||
| 6100 | |||
| 6101 | @property name |
||
| 6102 | @type {String} |
||
| 6103 | @default "" |
||
| 6104 | */ |
||
| 6105 | name: "", |
||
| 6106 | |||
| 6107 | /** |
||
| 6108 | Size of the image in bytes. Actual value is set only after image is preloaded. |
||
| 6109 | |||
| 6110 | @property size |
||
| 6111 | @type {Number} |
||
| 6112 | @default 0 |
||
| 6113 | */ |
||
| 6114 | size: 0, |
||
| 6115 | |||
| 6116 | /** |
||
| 6117 | Width of the image. Actual value is set only after image is preloaded. |
||
| 6118 | |||
| 6119 | @property width |
||
| 6120 | @type {Number} |
||
| 6121 | @default 0 |
||
| 6122 | */ |
||
| 6123 | width: 0, |
||
| 6124 | |||
| 6125 | /** |
||
| 6126 | Height of the image. Actual value is set only after image is preloaded. |
||
| 6127 | |||
| 6128 | @property height |
||
| 6129 | @type {Number} |
||
| 6130 | @default 0 |
||
| 6131 | */ |
||
| 6132 | height: 0, |
||
| 6133 | |||
| 6134 | /** |
||
| 6135 | Mime type of the image. Currently only image/jpeg and image/png are supported. Actual value is set only after image is preloaded. |
||
| 6136 | |||
| 6137 | @property type |
||
| 6138 | @type {String} |
||
| 6139 | @default "" |
||
| 6140 | */ |
||
| 6141 | type: "", |
||
| 6142 | |||
| 6143 | /** |
||
| 6144 | Holds meta info (Exif, GPS). Is populated only for image/jpeg. Actual value is set only after image is preloaded. |
||
| 6145 | |||
| 6146 | @property meta |
||
| 6147 | @type {Object} |
||
| 6148 | @default {} |
||
| 6149 | */ |
||
| 6150 | meta: {}, |
||
| 6151 | |||
| 6152 | /** |
||
| 6153 | Alias for load method, that takes another moxie.image.Image object as a source (see load). |
||
| 6154 | |||
| 6155 | @method clone |
||
| 6156 | @param {Image} src Source for the image |
||
| 6157 | @param {Boolean} [exact=false] Whether to activate in-depth clone mode |
||
| 6158 | */ |
||
| 6159 | clone: function() { |
||
| 6160 | this.load.apply(this, arguments); |
||
| 6161 | }, |
||
| 6162 | |||
| 6163 | /** |
||
| 6164 | Loads image from various sources. Currently the source for new image can be: moxie.image.Image, |
||
| 6165 | moxie.file.Blob/moxie.file.File, native Blob/File, dataUrl or URL. Depending on the type of the |
||
| 6166 | source, arguments - differ. When source is URL, Image will be downloaded from remote destination |
||
| 6167 | and loaded in memory. |
||
| 6168 | |||
| 6169 | @example |
||
| 6170 | var img = new moxie.image.Image(); |
||
| 6171 | img.onload = function() { |
||
| 6172 | var blob = img.getAsBlob(); |
||
| 6173 | |||
| 6174 | var formData = new moxie.xhr.FormData(); |
||
| 6175 | formData.append('file', blob); |
||
| 6176 | |||
| 6177 | var xhr = new moxie.xhr.XMLHttpRequest(); |
||
| 6178 | xhr.onload = function() { |
||
| 6179 | // upload complete |
||
| 6180 | }; |
||
| 6181 | xhr.open('post', 'upload.php'); |
||
| 6182 | xhr.send(formData); |
||
| 6183 | }; |
||
| 6184 | img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg) |
||
| 6185 | |||
| 6186 | |||
| 6187 | @method load |
||
| 6188 | @param {Image|Blob|File|String} src Source for the image |
||
| 6189 | @param {Boolean|Object} [mixed] |
||
| 6190 | */ |
||
| 6191 | load: function() { |
||
| 6192 | _load.apply(this, arguments); |
||
| 6193 | }, |
||
| 6194 | |||
| 6195 | |||
| 6196 | /** |
||
| 6197 | Resizes the image to fit the specified width/height. If crop is specified, image will also be |
||
| 6198 | cropped to the exact dimensions. |
||
| 6199 | |||
| 6200 | @method resize |
||
| 6201 | @since 3.0 |
||
| 6202 | @param {Object} options |
||
| 6203 | @param {Number} options.width Resulting width |
||
| 6204 | @param {Number} [options.height=width] Resulting height (optional, if not supplied will default to width) |
||
| 6205 | @param {String} [options.type='image/jpeg'] MIME type of the resulting image |
||
| 6206 | @param {Number} [options.quality=90] In the case of JPEG, controls the quality of resulting image |
||
| 6207 | @param {Boolean} [options.crop='cc'] If not falsy, image will be cropped, by default from center |
||
| 6208 | @param {Boolean} [options.fit=true] Whether to upscale the image to fit the exact dimensions |
||
| 6209 | @param {Boolean} [options.preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize) |
||
| 6210 | @param {String} [options.resample='default'] Resampling algorithm to use during resize |
||
| 6211 | @param {Boolean} [options.multipass=true] Whether to scale the image in steps (results in better quality) |
||
| 6212 | */ |
||
| 6213 | resize: function(options) { |
||
| 6214 | var self = this; |
||
| 6215 | var orientation; |
||
| 6216 | var scale; |
||
| 6217 | |||
| 6218 | var srcRect = { |
||
| 6219 | x: 0, |
||
| 6220 | y: 0, |
||
| 6221 | width: self.width, |
||
| 6222 | height: self.height |
||
| 6223 | }; |
||
| 6224 | |||
| 6225 | var opts = Basic.extendIf({ |
||
| 6226 | width: self.width, |
||
| 6227 | height: self.height, |
||
| 6228 | type: self.type || 'image/jpeg', |
||
| 6229 | quality: 90, |
||
| 6230 | crop: false, |
||
| 6231 | fit: true, |
||
| 6232 | preserveHeaders: true, |
||
| 6233 | resample: 'default', |
||
| 6234 | multipass: true |
||
| 6235 | }, options); |
||
| 6236 | |||
| 6237 | try { |
||
| 6238 | if (!self.size) { // only preloaded image objects can be used as source |
||
| 6239 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 6240 | } |
||
| 6241 | |||
| 6242 | // no way to reliably intercept the crash due to high resolution, so we simply avoid it |
||
| 6243 | if (self.width > Image.MAX_RESIZE_WIDTH || self.height > Image.MAX_RESIZE_HEIGHT) { |
||
| 6244 | throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR); |
||
| 6245 | } |
||
| 6246 | |||
| 6247 | // take into account orientation tag |
||
| 6248 | orientation = (self.meta && self.meta.tiff && self.meta.tiff.Orientation) || 1; |
||
| 6249 | |||
| 6250 | if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation |
||
| 6251 | var tmp = opts.width; |
||
| 6252 | opts.width = opts.height; |
||
| 6253 | opts.height = tmp; |
||
| 6254 | } |
||
| 6255 | |||
| 6256 | if (opts.crop) { |
||
| 6257 | scale = Math.max(opts.width/self.width, opts.height/self.height); |
||
| 6258 | |||
| 6259 | if (options.fit) { |
||
| 6260 | // first scale it up or down to fit the original image |
||
| 6261 | srcRect.width = Math.min(Math.ceil(opts.width/scale), self.width); |
||
| 6262 | srcRect.height = Math.min(Math.ceil(opts.height/scale), self.height); |
||
| 6263 | |||
| 6264 | // recalculate the scale for adapted dimensions |
||
| 6265 | scale = opts.width/srcRect.width; |
||
| 6266 | } else { |
||
| 6267 | srcRect.width = Math.min(opts.width, self.width); |
||
| 6268 | srcRect.height = Math.min(opts.height, self.height); |
||
| 6269 | |||
| 6270 | // now we do not need to scale it any further |
||
| 6271 | scale = 1; |
||
| 6272 | } |
||
| 6273 | |||
| 6274 | if (typeof(opts.crop) === 'boolean') { |
||
| 6275 | opts.crop = 'cc'; |
||
| 6276 | } |
||
| 6277 | |||
| 6278 | switch (opts.crop.toLowerCase().replace(/_/, '-')) { |
||
| 6279 | case 'rb': |
||
| 6280 | case 'right-bottom': |
||
| 6281 | srcRect.x = self.width - srcRect.width; |
||
| 6282 | srcRect.y = self.height - srcRect.height; |
||
| 6283 | break; |
||
| 6284 | |||
| 6285 | case 'cb': |
||
| 6286 | case 'center-bottom': |
||
| 6287 | srcRect.x = Math.floor((self.width - srcRect.width) / 2); |
||
| 6288 | srcRect.y = self.height - srcRect.height; |
||
| 6289 | break; |
||
| 6290 | |||
| 6291 | case 'lb': |
||
| 6292 | case 'left-bottom': |
||
| 6293 | srcRect.x = 0; |
||
| 6294 | srcRect.y = self.height - srcRect.height; |
||
| 6295 | break; |
||
| 6296 | |||
| 6297 | case 'lt': |
||
| 6298 | case 'left-top': |
||
| 6299 | srcRect.x = 0; |
||
| 6300 | srcRect.y = 0; |
||
| 6301 | break; |
||
| 6302 | |||
| 6303 | case 'ct': |
||
| 6304 | case 'center-top': |
||
| 6305 | srcRect.x = Math.floor((self.width - srcRect.width) / 2); |
||
| 6306 | srcRect.y = 0; |
||
| 6307 | break; |
||
| 6308 | |||
| 6309 | case 'rt': |
||
| 6310 | case 'right-top': |
||
| 6311 | srcRect.x = self.width - srcRect.width; |
||
| 6312 | srcRect.y = 0; |
||
| 6313 | break; |
||
| 6314 | |||
| 6315 | case 'rc': |
||
| 6316 | case 'right-center': |
||
| 6317 | case 'right-middle': |
||
| 6318 | srcRect.x = self.width - srcRect.width; |
||
| 6319 | srcRect.y = Math.floor((self.height - srcRect.height) / 2); |
||
| 6320 | break; |
||
| 6321 | |||
| 6322 | |||
| 6323 | case 'lc': |
||
| 6324 | case 'left-center': |
||
| 6325 | case 'left-middle': |
||
| 6326 | srcRect.x = 0; |
||
| 6327 | srcRect.y = Math.floor((self.height - srcRect.height) / 2); |
||
| 6328 | break; |
||
| 6329 | |||
| 6330 | case 'cc': |
||
| 6331 | case 'center-center': |
||
| 6332 | case 'center-middle': |
||
| 6333 | default: |
||
| 6334 | srcRect.x = Math.floor((self.width - srcRect.width) / 2); |
||
| 6335 | srcRect.y = Math.floor((self.height - srcRect.height) / 2); |
||
| 6336 | } |
||
| 6337 | |||
| 6338 | // original image might be smaller than requested crop, so - avoid negative values |
||
| 6339 | srcRect.x = Math.max(srcRect.x, 0); |
||
| 6340 | srcRect.y = Math.max(srcRect.y, 0); |
||
| 6341 | } else { |
||
| 6342 | scale = Math.min(opts.width/self.width, opts.height/self.height); |
||
| 6343 | |||
| 6344 | // do not upscale if we were asked to not fit it |
||
| 6345 | if (scale > 1 && !opts.fit) { |
||
| 6346 | scale = 1; |
||
| 6347 | } |
||
| 6348 | } |
||
| 6349 | |||
| 6350 | this.exec('Image', 'resize', srcRect, scale, opts); |
||
| 6351 | } catch(ex) { |
||
| 6352 | // for now simply trigger error event |
||
| 6353 | self.trigger('error', ex.code); |
||
| 6354 | } |
||
| 6355 | }, |
||
| 6356 | |||
| 6357 | /** |
||
| 6358 | Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions. |
||
| 6359 | |||
| 6360 | @method downsize |
||
| 6361 | @deprecated use resize() |
||
| 6362 | */ |
||
| 6363 | downsize: function(options) { |
||
| 6364 | var defaults = { |
||
| 6365 | width: this.width, |
||
| 6366 | height: this.height, |
||
| 6367 | type: this.type || 'image/jpeg', |
||
| 6368 | quality: 90, |
||
| 6369 | crop: false, |
||
| 6370 | fit: false, |
||
| 6371 | preserveHeaders: true, |
||
| 6372 | resample: 'default' |
||
| 6373 | }, opts; |
||
| 6374 | |||
| 6375 | if (typeof(options) === 'object') { |
||
| 6376 | opts = Basic.extend(defaults, options); |
||
| 6377 | } else { |
||
| 6378 | // for backward compatibility |
||
| 6379 | opts = Basic.extend(defaults, { |
||
| 6380 | width: arguments[0], |
||
| 6381 | height: arguments[1], |
||
| 6382 | crop: arguments[2], |
||
| 6383 | preserveHeaders: arguments[3] |
||
| 6384 | }); |
||
| 6385 | } |
||
| 6386 | |||
| 6387 | this.resize(opts); |
||
| 6388 | }, |
||
| 6389 | |||
| 6390 | /** |
||
| 6391 | Alias for downsize(width, height, true). (see downsize) |
||
| 6392 | |||
| 6393 | @method crop |
||
| 6394 | @param {Number} width Resulting width |
||
| 6395 | @param {Number} [height=width] Resulting height (optional, if not supplied will default to width) |
||
| 6396 | @param {Boolean} [preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize) |
||
| 6397 | */ |
||
| 6398 | crop: function(width, height, preserveHeaders) { |
||
| 6399 | this.downsize(width, height, true, preserveHeaders); |
||
| 6400 | }, |
||
| 6401 | |||
| 6402 | getAsCanvas: function() { |
||
| 6403 | if (!Env.can('create_canvas')) { |
||
| 6404 | throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR); |
||
| 6405 | } |
||
| 6406 | return this.exec('Image', 'getAsCanvas'); |
||
| 6407 | }, |
||
| 6408 | |||
| 6409 | /** |
||
| 6410 | Retrieves image in it's current state as moxie.file.Blob object. Cannot be run on empty or image in progress (throws |
||
| 6411 | DOMException.INVALID_STATE_ERR). |
||
| 6412 | |||
| 6413 | @method getAsBlob |
||
| 6414 | @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png |
||
| 6415 | @param {Number} [quality=90] Applicable only together with mime type image/jpeg |
||
| 6416 | @return {Blob} Image as Blob |
||
| 6417 | */ |
||
| 6418 | getAsBlob: function(type, quality) { |
||
| 6419 | if (!this.size) { |
||
| 6420 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 6421 | } |
||
| 6422 | return this.exec('Image', 'getAsBlob', type || 'image/jpeg', quality || 90); |
||
| 6423 | }, |
||
| 6424 | |||
| 6425 | /** |
||
| 6426 | Retrieves image in it's current state as dataURL string. Cannot be run on empty or image in progress (throws |
||
| 6427 | DOMException.INVALID_STATE_ERR). |
||
| 6428 | |||
| 6429 | @method getAsDataURL |
||
| 6430 | @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png |
||
| 6431 | @param {Number} [quality=90] Applicable only together with mime type image/jpeg |
||
| 6432 | @return {String} Image as dataURL string |
||
| 6433 | */ |
||
| 6434 | getAsDataURL: function(type, quality) { |
||
| 6435 | if (!this.size) { |
||
| 6436 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 6437 | } |
||
| 6438 | return this.exec('Image', 'getAsDataURL', type || 'image/jpeg', quality || 90); |
||
| 6439 | }, |
||
| 6440 | |||
| 6441 | /** |
||
| 6442 | Retrieves image in it's current state as binary string. Cannot be run on empty or image in progress (throws |
||
| 6443 | DOMException.INVALID_STATE_ERR). |
||
| 6444 | |||
| 6445 | @method getAsBinaryString |
||
| 6446 | @param {String} [type="image/jpeg"] Mime type of resulting blob. Can either be image/jpeg or image/png |
||
| 6447 | @param {Number} [quality=90] Applicable only together with mime type image/jpeg |
||
| 6448 | @return {String} Image as binary string |
||
| 6449 | */ |
||
| 6450 | getAsBinaryString: function(type, quality) { |
||
| 6451 | var dataUrl = this.getAsDataURL(type, quality); |
||
| 6452 | return Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)); |
||
| 6453 | }, |
||
| 6454 | |||
| 6455 | /** |
||
| 6456 | Embeds a visual representation of the image into the specified node. Depending on the runtime, |
||
| 6457 | it might be a canvas, an img node or a thrid party shim object (Flash or SilverLight - very rare, |
||
| 6458 | can be used in legacy browsers that do not have canvas or proper dataURI support). |
||
| 6459 | |||
| 6460 | @method embed |
||
| 6461 | @param {DOMElement} el DOM element to insert the image object into |
||
| 6462 | @param {Object} [options] |
||
| 6463 | @param {Number} [options.width] The width of an embed (defaults to the image width) |
||
| 6464 | @param {Number} [options.height] The height of an embed (defaults to the image height) |
||
| 6465 | @param {String} [options.type="image/jpeg"] Mime type |
||
| 6466 | @param {Number} [options.quality=90] Quality of an embed, if mime type is image/jpeg |
||
| 6467 | @param {Boolean} [options.crop=false] Whether to crop an embed to the specified dimensions |
||
| 6468 | @param {Boolean} [options.fit=true] By default thumbs will be up- or downscaled as necessary to fit the dimensions |
||
| 6469 | */ |
||
| 6470 | embed: function(el, options) { |
||
| 6471 | var self = this |
||
| 6472 | , runtime // this has to be outside of all the closures to contain proper runtime |
||
| 6473 | ; |
||
| 6474 | |||
| 6475 | var opts = Basic.extend({ |
||
| 6476 | width: this.width, |
||
| 6477 | height: this.height, |
||
| 6478 | type: this.type || 'image/jpeg', |
||
| 6479 | quality: 90, |
||
| 6480 | fit: true, |
||
| 6481 | resample: 'nearest' |
||
| 6482 | }, options); |
||
| 6483 | |||
| 6484 | |||
| 6485 | function render(type, quality) { |
||
| 6486 | var img = this; |
||
| 6487 | |||
| 6488 | // if possible, embed a canvas element directly |
||
| 6489 | if (Env.can('create_canvas')) { |
||
| 6490 | var canvas = img.getAsCanvas(); |
||
| 6491 | if (canvas) { |
||
| 6492 | el.appendChild(canvas); |
||
| 6493 | canvas = null; |
||
| 6494 | img.destroy(); |
||
| 6495 | self.trigger('embedded'); |
||
| 6496 | return; |
||
| 6497 | } |
||
| 6498 | } |
||
| 6499 | |||
| 6500 | var dataUrl = img.getAsDataURL(type, quality); |
||
| 6501 | if (!dataUrl) { |
||
| 6502 | throw new x.ImageError(x.ImageError.WRONG_FORMAT); |
||
| 6503 | } |
||
| 6504 | |||
| 6505 | if (Env.can('use_data_uri_of', dataUrl.length)) { |
||
| 6506 | el.innerHTML = '<img src="' + dataUrl + '" width="' + img.width + '" height="' + img.height + '" alt="" />'; |
||
| 6507 | img.destroy(); |
||
| 6508 | self.trigger('embedded'); |
||
| 6509 | } else { |
||
| 6510 | var tr = new Transporter(); |
||
| 6511 | |||
| 6512 | tr.bind("TransportingComplete", function() { |
||
| 6513 | runtime = self.connectRuntime(this.result.ruid); |
||
| 6514 | |||
| 6515 | self.bind("Embedded", function() { |
||
| 6516 | // position and size properly |
||
| 6517 | Basic.extend(runtime.getShimContainer().style, { |
||
| 6518 | //position: 'relative', |
||
| 6519 | top: '0px', |
||
| 6520 | left: '0px', |
||
| 6521 | width: img.width + 'px', |
||
| 6522 | height: img.height + 'px' |
||
| 6523 | }); |
||
| 6524 | |||
| 6525 | // some shims (Flash/SilverLight) reinitialize, if parent element is hidden, reordered or it's |
||
| 6526 | // position type changes (in Gecko), but since we basically need this only in IEs 6/7 and |
||
| 6527 | // sometimes 8 and they do not have this problem, we can comment this for now |
||
| 6528 | /*tr.bind("RuntimeInit", function(e, runtime) { |
||
| 6529 | tr.destroy(); |
||
| 6530 | runtime.destroy(); |
||
| 6531 | onResize.call(self); // re-feed our image data |
||
| 6532 | });*/ |
||
| 6533 | |||
| 6534 | runtime = null; // release |
||
| 6535 | }, 999); |
||
| 6536 | |||
| 6537 | runtime.exec.call(self, "ImageView", "display", this.result.uid, width, height); |
||
| 6538 | img.destroy(); |
||
| 6539 | }); |
||
| 6540 | |||
| 6541 | tr.transport(Encode.atob(dataUrl.substring(dataUrl.indexOf('base64,') + 7)), type, { |
||
| 6542 | required_caps: { |
||
| 6543 | display_media: true |
||
| 6544 | }, |
||
| 6545 | runtime_order: 'flash,silverlight', |
||
| 6546 | container: el |
||
| 6547 | }); |
||
| 6548 | } |
||
| 6549 | } |
||
| 6550 | |||
| 6551 | try { |
||
| 6552 | if (!(el = Dom.get(el))) { |
||
| 6553 | throw new x.DOMException(x.DOMException.INVALID_NODE_TYPE_ERR); |
||
| 6554 | } |
||
| 6555 | |||
| 6556 | if (!this.size) { // only preloaded image objects can be used as source |
||
| 6557 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 6558 | } |
||
| 6559 | |||
| 6560 | // high-resolution images cannot be consistently handled across the runtimes |
||
| 6561 | if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) { |
||
| 6562 | //throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR); |
||
| 6563 | } |
||
| 6564 | |||
| 6565 | var imgCopy = new Image(); |
||
| 6566 | |||
| 6567 | imgCopy.bind("Resize", function() { |
||
| 6568 | render.call(this, opts.type, opts.quality); |
||
| 6569 | }); |
||
| 6570 | |||
| 6571 | imgCopy.bind("Load", function() { |
||
| 6572 | this.downsize(opts); |
||
| 6573 | }); |
||
| 6574 | |||
| 6575 | // if embedded thumb data is available and dimensions are big enough, use it |
||
| 6576 | if (this.meta.thumb && this.meta.thumb.width >= opts.width && this.meta.thumb.height >= opts.height) { |
||
| 6577 | imgCopy.load(this.meta.thumb.data); |
||
| 6578 | } else { |
||
| 6579 | imgCopy.clone(this, false); |
||
| 6580 | } |
||
| 6581 | |||
| 6582 | return imgCopy; |
||
| 6583 | } catch(ex) { |
||
| 6584 | // for now simply trigger error event |
||
| 6585 | this.trigger('error', ex.code); |
||
| 6586 | } |
||
| 6587 | }, |
||
| 6588 | |||
| 6589 | /** |
||
| 6590 | Properly destroys the image and frees resources in use. If any. Recommended way to dispose |
||
| 6591 | moxie.image.Image object. |
||
| 6592 | |||
| 6593 | @method destroy |
||
| 6594 | */ |
||
| 6595 | destroy: function() { |
||
| 6596 | if (this.ruid) { |
||
| 6597 | this.getRuntime().exec.call(this, 'Image', 'destroy'); |
||
| 6598 | this.disconnectRuntime(); |
||
| 6599 | } |
||
| 6600 | if (this.meta && this.meta.thumb) { |
||
| 6601 | // thumb is blob, make sure we destroy it first |
||
| 6602 | this.meta.thumb.data.destroy(); |
||
| 6603 | } |
||
| 6604 | this.unbindAll(); |
||
| 6605 | } |
||
| 6606 | }); |
||
| 6607 | |||
| 6608 | |||
| 6609 | // this is here, because in order to bind properly, we need uid, which is created above |
||
| 6610 | this.handleEventProps(dispatches); |
||
| 6611 | |||
| 6612 | this.bind('Load Resize', function() { |
||
| 6613 | return _updateInfo.call(this); // if operation fails (e.g. image is neither PNG nor JPEG) cancel all pending events |
||
| 6614 | }, 999); |
||
| 6615 | |||
| 6616 | |||
| 6617 | function _updateInfo(info) { |
||
| 6618 | try { |
||
| 6619 | if (!info) { |
||
| 6620 | info = this.exec('Image', 'getInfo'); |
||
| 6621 | } |
||
| 6622 | |||
| 6623 | this.size = info.size; |
||
| 6624 | this.width = info.width; |
||
| 6625 | this.height = info.height; |
||
| 6626 | this.type = info.type; |
||
| 6627 | this.meta = info.meta; |
||
| 6628 | |||
| 6629 | // update file name, only if empty |
||
| 6630 | if (this.name === '') { |
||
| 6631 | this.name = info.name; |
||
| 6632 | } |
||
| 6633 | |||
| 6634 | return true; |
||
| 6635 | } catch(ex) { |
||
| 6636 | this.trigger('error', ex.code); |
||
| 6637 | return false; |
||
| 6638 | } |
||
| 6639 | } |
||
| 6640 | |||
| 6641 | |||
| 6642 | function _load(src) { |
||
| 6643 | var srcType = Basic.typeOf(src); |
||
| 6644 | |||
| 6645 | try { |
||
| 6646 | // if source is Image |
||
| 6647 | if (src instanceof Image) { |
||
| 6648 | if (!src.size) { // only preloaded image objects can be used as source |
||
| 6649 | throw new x.DOMException(x.DOMException.INVALID_STATE_ERR); |
||
| 6650 | } |
||
| 6651 | _loadFromImage.apply(this, arguments); |
||
| 6652 | } |
||
| 6653 | // if source is o.Blob/o.File |
||
| 6654 | else if (src instanceof Blob) { |
||
| 6655 | if (!~Basic.inArray(src.type, ['image/jpeg', 'image/png'])) { |
||
| 6656 | throw new x.ImageError(x.ImageError.WRONG_FORMAT); |
||
| 6657 | } |
||
| 6658 | _loadFromBlob.apply(this, arguments); |
||
| 6659 | } |
||
| 6660 | // if native blob/file |
||
| 6661 | else if (Basic.inArray(srcType, ['blob', 'file']) !== -1) { |
||
| 6662 | _load.call(this, new File(null, src), arguments[1]); |
||
| 6663 | } |
||
| 6664 | // if String |
||
| 6665 | else if (srcType === 'string') { |
||
| 6666 | // if dataUrl String |
||
| 6667 | if (src.substr(0, 5) === 'data:') { |
||
| 6668 | _load.call(this, new Blob(null, { data: src }), arguments[1]); |
||
| 6669 | } |
||
| 6670 | // else assume Url, either relative or absolute |
||
| 6671 | else { |
||
| 6672 | _loadFromUrl.apply(this, arguments); |
||
| 6673 | } |
||
| 6674 | } |
||
| 6675 | // if source seems to be an img node |
||
| 6676 | else if (srcType === 'node' && src.nodeName.toLowerCase() === 'img') { |
||
| 6677 | _load.call(this, src.src, arguments[1]); |
||
| 6678 | } |
||
| 6679 | else { |
||
| 6680 | throw new x.DOMException(x.DOMException.TYPE_MISMATCH_ERR); |
||
| 6681 | } |
||
| 6682 | } catch(ex) { |
||
| 6683 | // for now simply trigger error event |
||
| 6684 | this.trigger('error', ex.code); |
||
| 6685 | } |
||
| 6686 | } |
||
| 6687 | |||
| 6688 | |||
| 6689 | function _loadFromImage(img, exact) { |
||
| 6690 | var runtime = this.connectRuntime(img.ruid); |
||
| 6691 | this.ruid = runtime.uid; |
||
| 6692 | runtime.exec.call(this, 'Image', 'loadFromImage', img, (Basic.typeOf(exact) === 'undefined' ? true : exact)); |
||
| 6693 | } |
||
| 6694 | |||
| 6695 | |||
| 6696 | function _loadFromBlob(blob, options) { |
||
| 6697 | var self = this; |
||
| 6698 | |||
| 6699 | self.name = blob.name || ''; |
||
| 6700 | |||
| 6701 | function exec(runtime) { |
||
| 6702 | self.ruid = runtime.uid; |
||
| 6703 | runtime.exec.call(self, 'Image', 'loadFromBlob', blob); |
||
| 6704 | } |
||
| 6705 | |||
| 6706 | if (blob.isDetached()) { |
||
| 6707 | this.bind('RuntimeInit', function(e, runtime) { |
||
| 6708 | exec(runtime); |
||
| 6709 | }); |
||
| 6710 | |||
| 6711 | // convert to object representation |
||
| 6712 | if (options && typeof(options.required_caps) === 'string') { |
||
| 6713 | options.required_caps = Runtime.parseCaps(options.required_caps); |
||
| 6714 | } |
||
| 6715 | |||
| 6716 | this.connectRuntime(Basic.extend({ |
||
| 6717 | required_caps: { |
||
| 6718 | access_image_binary: true, |
||
| 6719 | resize_image: true |
||
| 6720 | } |
||
| 6721 | }, options)); |
||
| 6722 | } else { |
||
| 6723 | exec(this.connectRuntime(blob.ruid)); |
||
| 6724 | } |
||
| 6725 | } |
||
| 6726 | |||
| 6727 | |||
| 6728 | function _loadFromUrl(url, options) { |
||
| 6729 | var self = this, xhr; |
||
| 6730 | |||
| 6731 | xhr = new XMLHttpRequest(); |
||
| 6732 | |||
| 6733 | xhr.open('get', url); |
||
| 6734 | xhr.responseType = 'blob'; |
||
| 6735 | |||
| 6736 | xhr.onprogress = function(e) { |
||
| 6737 | self.trigger(e); |
||
| 6738 | }; |
||
| 6739 | |||
| 6740 | xhr.onload = function() { |
||
| 6741 | _loadFromBlob.call(self, xhr.response, true); |
||
| 6742 | }; |
||
| 6743 | |||
| 6744 | xhr.onerror = function(e) { |
||
| 6745 | self.trigger(e); |
||
| 6746 | }; |
||
| 6747 | |||
| 6748 | xhr.onloadend = function() { |
||
| 6749 | xhr.destroy(); |
||
| 6750 | }; |
||
| 6751 | |||
| 6752 | xhr.bind('RuntimeError', function(e, err) { |
||
| 6753 | self.trigger('RuntimeError', err); |
||
| 6754 | }); |
||
| 6755 | |||
| 6756 | xhr.send(null, options); |
||
| 6757 | } |
||
| 6758 | } |
||
| 6759 | |||
| 6760 | // virtual world will crash on you if image has a resolution higher than this: |
||
| 6761 | Image.MAX_RESIZE_WIDTH = 8192; |
||
| 6762 | Image.MAX_RESIZE_HEIGHT = 8192; |
||
| 6763 | |||
| 6764 | Image.prototype = EventTarget.instance; |
||
| 6765 | |||
| 6766 | return Image; |
||
| 6767 | }); |
||
| 6768 | |||
| 6769 | // Included from: src/javascript/runtime/html5/Runtime.js |
||
| 6770 | |||
| 6771 | /** |
||
| 6772 | * Runtime.js |
||
| 6773 | * |
||
| 6774 | * Copyright 2013, Moxiecode Systems AB |
||
| 6775 | * Released under GPL License. |
||
| 6776 | * |
||
| 6777 | * License: http://www.plupload.com/license |
||
| 6778 | * Contributing: http://www.plupload.com/contributing |
||
| 6779 | */ |
||
| 6780 | |||
| 6781 | /*global File:true */ |
||
| 6782 | |||
| 6783 | /** |
||
| 6784 | Defines constructor for HTML5 runtime. |
||
| 6785 | |||
| 6786 | @class moxie/runtime/html5/Runtime |
||
| 6787 | @private |
||
| 6788 | */ |
||
| 6789 | define("moxie/runtime/html5/Runtime", [ |
||
| 6790 | "moxie/core/utils/Basic", |
||
| 6791 | "moxie/core/Exceptions", |
||
| 6792 | "moxie/runtime/Runtime", |
||
| 6793 | "moxie/core/utils/Env" |
||
| 6794 | ], function(Basic, x, Runtime, Env) { |
||
| 6795 | |||
| 6796 | var type = "html5", extensions = {}; |
||
| 6797 | |||
| 6798 | function Html5Runtime(options) { |
||
| 6799 | var I = this |
||
| 6800 | , Test = Runtime.capTest |
||
| 6801 | , True = Runtime.capTrue |
||
| 6802 | ; |
||
| 6803 | |||
| 6804 | var caps = Basic.extend({ |
||
| 6805 | access_binary: Test(window.FileReader || window.File && window.File.getAsDataURL), |
||
| 6806 | access_image_binary: function() { |
||
| 6807 | return I.can('access_binary') && !!extensions.Image; |
||
| 6808 | }, |
||
| 6809 | display_media: Test( |
||
| 6810 | (Env.can('create_canvas') || Env.can('use_data_uri_over32kb')) && |
||
| 6811 | defined('moxie/image/Image') |
||
| 6812 | ), |
||
| 6813 | do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()), |
||
| 6814 | drag_and_drop: Test(function() { |
||
| 6815 | // this comes directly from Modernizr: http://www.modernizr.com/ |
||
| 6816 | var div = document.createElement('div'); |
||
| 6817 | // IE has support for drag and drop since version 5, but doesn't support dropping files from desktop |
||
| 6818 | return (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && |
||
| 6819 | (Env.browser !== 'IE' || Env.verComp(Env.version, 9, '>')); |
||
| 6820 | }()), |
||
| 6821 | filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest |
||
| 6822 | return !( |
||
| 6823 | (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '<')) || |
||
| 6824 | (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) || |
||
| 6825 | (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '<')) || |
||
| 6826 | (Env.browser === 'Firefox' && Env.verComp(Env.version, 37, '<')) |
||
| 6827 | ); |
||
| 6828 | }()), |
||
| 6829 | return_response_headers: True, |
||
| 6830 | return_response_type: function(responseType) { |
||
| 6831 | if (responseType === 'json' && !!window.JSON) { // we can fake this one even if it's not supported |
||
| 6832 | return true; |
||
| 6833 | } |
||
| 6834 | return Env.can('return_response_type', responseType); |
||
| 6835 | }, |
||
| 6836 | return_status_code: True, |
||
| 6837 | report_upload_progress: Test(window.XMLHttpRequest && new XMLHttpRequest().upload), |
||
| 6838 | resize_image: function() { |
||
| 6839 | return I.can('access_binary') && Env.can('create_canvas'); |
||
| 6840 | }, |
||
| 6841 | select_file: function() { |
||
| 6842 | return Env.can('use_fileinput') && window.File; |
||
| 6843 | }, |
||
| 6844 | select_folder: function() { |
||
| 6845 | return I.can('select_file') && ( |
||
| 6846 | Env.browser === 'Chrome' && Env.verComp(Env.version, 21, '>=') || |
||
| 6847 | Env.browser === 'Firefox' && Env.verComp(Env.version, 42, '>=') // https://developer.mozilla.org/en-US/Firefox/Releases/42 |
||
| 6848 | ); |
||
| 6849 | }, |
||
| 6850 | select_multiple: function() { |
||
| 6851 | // it is buggy on Safari Windows and iOS |
||
| 6852 | return I.can('select_file') && |
||
| 6853 | !(Env.browser === 'Safari' && Env.os === 'Windows') && |
||
| 6854 | !(Env.os === 'iOS' && Env.verComp(Env.osVersion, "7.0.0", '>') && Env.verComp(Env.osVersion, "8.0.0", '<')); |
||
| 6855 | }, |
||
| 6856 | send_binary_string: Test(window.XMLHttpRequest && (new XMLHttpRequest().sendAsBinary || (window.Uint8Array && window.ArrayBuffer))), |
||
| 6857 | send_custom_headers: Test(window.XMLHttpRequest), |
||
| 6858 | send_multipart: function() { |
||
| 6859 | return !!(window.XMLHttpRequest && new XMLHttpRequest().upload && window.FormData) || I.can('send_binary_string'); |
||
| 6860 | }, |
||
| 6861 | slice_blob: Test(window.File && (File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice)), |
||
| 6862 | stream_upload: function(){ |
||
| 6863 | return I.can('slice_blob') && I.can('send_multipart'); |
||
| 6864 | }, |
||
| 6865 | summon_file_dialog: function() { // yeah... some dirty sniffing here... |
||
| 6866 | return I.can('select_file') && !( |
||
| 6867 | (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '<')) || |
||
| 6868 | (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '<')) || |
||
| 6869 | (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) |
||
| 6870 | ); |
||
| 6871 | }, |
||
| 6872 | upload_filesize: True, |
||
| 6873 | use_http_method: True |
||
| 6874 | }, |
||
| 6875 | arguments[2] |
||
| 6876 | ); |
||
| 6877 | |||
| 6878 | Runtime.call(this, options, (arguments[1] || type), caps); |
||
| 6879 | |||
| 6880 | |||
| 6881 | Basic.extend(this, { |
||
| 6882 | |||
| 6883 | init : function() { |
||
| 6884 | this.trigger("Init"); |
||
| 6885 | }, |
||
| 6886 | |||
| 6887 | destroy: (function(destroy) { // extend default destroy method |
||
| 6888 | return function() { |
||
| 6889 | destroy.call(I); |
||
| 6890 | destroy = I = null; |
||
| 6891 | }; |
||
| 6892 | }(this.destroy)) |
||
| 6893 | }); |
||
| 6894 | |||
| 6895 | Basic.extend(this.getShim(), extensions); |
||
| 6896 | } |
||
| 6897 | |||
| 6898 | Runtime.addConstructor(type, Html5Runtime); |
||
| 6899 | |||
| 6900 | return extensions; |
||
| 6901 | }); |
||
| 6902 | |||
| 6903 | // Included from: src/javascript/runtime/html5/file/Blob.js |
||
| 6904 | |||
| 6905 | /** |
||
| 6906 | * Blob.js |
||
| 6907 | * |
||
| 6908 | * Copyright 2013, Moxiecode Systems AB |
||
| 6909 | * Released under GPL License. |
||
| 6910 | * |
||
| 6911 | * License: http://www.plupload.com/license |
||
| 6912 | * Contributing: http://www.plupload.com/contributing |
||
| 6913 | */ |
||
| 6914 | |||
| 6915 | /** |
||
| 6916 | @class moxie/runtime/html5/file/Blob |
||
| 6917 | @private |
||
| 6918 | */ |
||
| 6919 | define("moxie/runtime/html5/file/Blob", [ |
||
| 6920 | "moxie/runtime/html5/Runtime", |
||
| 6921 | "moxie/file/Blob" |
||
| 6922 | ], function(extensions, Blob) { |
||
| 6923 | |||
| 6924 | function HTML5Blob() { |
||
| 6925 | function w3cBlobSlice(blob, start, end) { |
||
| 6926 | var blobSlice; |
||
| 6927 | |||
| 6928 | if (window.File.prototype.slice) { |
||
| 6929 | try { |
||
| 6930 | blob.slice(); // depricated version will throw WRONG_ARGUMENTS_ERR exception |
||
| 6931 | return blob.slice(start, end); |
||
| 6932 | } catch (e) { |
||
| 6933 | // depricated slice method |
||
| 6934 | return blob.slice(start, end - start); |
||
| 6935 | } |
||
| 6936 | // slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672 |
||
| 6937 | } else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) { |
||
| 6938 | return blobSlice.call(blob, start, end); |
||
| 6939 | } else { |
||
| 6940 | return null; // or throw some exception |
||
| 6941 | } |
||
| 6942 | } |
||
| 6943 | |||
| 6944 | this.slice = function() { |
||
| 6945 | return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments)); |
||
| 6946 | }; |
||
| 6947 | |||
| 6948 | this.destroy = function() { |
||
| 6949 | this.getRuntime().getShim().removeInstance(this.uid); |
||
| 6950 | }; |
||
| 6951 | } |
||
| 6952 | |||
| 6953 | return (extensions.Blob = HTML5Blob); |
||
| 6954 | }); |
||
| 6955 | |||
| 6956 | // Included from: src/javascript/core/utils/Events.js |
||
| 6957 | |||
| 6958 | /** |
||
| 6959 | * Events.js |
||
| 6960 | * |
||
| 6961 | * Copyright 2013, Moxiecode Systems AB |
||
| 6962 | * Released under GPL License. |
||
| 6963 | * |
||
| 6964 | * License: http://www.plupload.com/license |
||
| 6965 | * Contributing: http://www.plupload.com/contributing |
||
| 6966 | */ |
||
| 6967 | |||
| 6968 | /** |
||
| 6969 | @class moxie/core/utils/Events |
||
| 6970 | @public |
||
| 6971 | @static |
||
| 6972 | */ |
||
| 6973 | |||
| 6974 | define('moxie/core/utils/Events', [ |
||
| 6975 | 'moxie/core/utils/Basic' |
||
| 6976 | ], function(Basic) { |
||
| 6977 | var eventhash = {}, uid = 'moxie_' + Basic.guid(); |
||
| 6978 | |||
| 6979 | // IE W3C like event funcs |
||
| 6980 | function preventDefault() { |
||
| 6981 | this.returnValue = false; |
||
| 6982 | } |
||
| 6983 | |||
| 6984 | function stopPropagation() { |
||
| 6985 | this.cancelBubble = true; |
||
| 6986 | } |
||
| 6987 | |||
| 6988 | /** |
||
| 6989 | Adds an event handler to the specified object and store reference to the handler |
||
| 6990 | in objects internal Plupload registry (@see removeEvent). |
||
| 6991 | |||
| 6992 | @method addEvent |
||
| 6993 | @static |
||
| 6994 | @param {Object} obj DOM element like object to add handler to. |
||
| 6995 | @param {String} name Name to add event listener to. |
||
| 6996 | @param {Function} callback Function to call when event occurs. |
||
| 6997 | @param {String} [key] that might be used to add specifity to the event record. |
||
| 6998 | */ |
||
| 6999 | var addEvent = function(obj, name, callback, key) { |
||
| 7000 | var func, events; |
||
| 7001 | |||
| 7002 | name = name.toLowerCase(); |
||
| 7003 | |||
| 7004 | // Add event listener |
||
| 7005 | if (obj.addEventListener) { |
||
| 7006 | func = callback; |
||
| 7007 | |||
| 7008 | obj.addEventListener(name, func, false); |
||
| 7009 | } else if (obj.attachEvent) { |
||
| 7010 | func = function() { |
||
| 7011 | var evt = window.event; |
||
| 7012 | |||
| 7013 | if (!evt.target) { |
||
| 7014 | evt.target = evt.srcElement; |
||
| 7015 | } |
||
| 7016 | |||
| 7017 | evt.preventDefault = preventDefault; |
||
| 7018 | evt.stopPropagation = stopPropagation; |
||
| 7019 | |||
| 7020 | callback(evt); |
||
| 7021 | }; |
||
| 7022 | |||
| 7023 | obj.attachEvent('on' + name, func); |
||
| 7024 | } |
||
| 7025 | |||
| 7026 | // Log event handler to objects internal mOxie registry |
||
| 7027 | if (!obj[uid]) { |
||
| 7028 | obj[uid] = Basic.guid(); |
||
| 7029 | } |
||
| 7030 | |||
| 7031 | if (!eventhash.hasOwnProperty(obj[uid])) { |
||
| 7032 | eventhash[obj[uid]] = {}; |
||
| 7033 | } |
||
| 7034 | |||
| 7035 | events = eventhash[obj[uid]]; |
||
| 7036 | |||
| 7037 | if (!events.hasOwnProperty(name)) { |
||
| 7038 | events[name] = []; |
||
| 7039 | } |
||
| 7040 | |||
| 7041 | events[name].push({ |
||
| 7042 | func: func, |
||
| 7043 | orig: callback, // store original callback for IE |
||
| 7044 | key: key |
||
| 7045 | }); |
||
| 7046 | }; |
||
| 7047 | |||
| 7048 | |||
| 7049 | /** |
||
| 7050 | Remove event handler from the specified object. If third argument (callback) |
||
| 7051 | is not specified remove all events with the specified name. |
||
| 7052 | |||
| 7053 | @method removeEvent |
||
| 7054 | @static |
||
| 7055 | @param {Object} obj DOM element to remove event listener(s) from. |
||
| 7056 | @param {String} name Name of event listener to remove. |
||
| 7057 | @param {Function|String} [callback] might be a callback or unique key to match. |
||
| 7058 | */ |
||
| 7059 | var removeEvent = function(obj, name, callback) { |
||
| 7060 | var type, undef; |
||
| 7061 | |||
| 7062 | name = name.toLowerCase(); |
||
| 7063 | |||
| 7064 | if (obj[uid] && eventhash[obj[uid]] && eventhash[obj[uid]][name]) { |
||
| 7065 | type = eventhash[obj[uid]][name]; |
||
| 7066 | } else { |
||
| 7067 | return; |
||
| 7068 | } |
||
| 7069 | |||
| 7070 | for (var i = type.length - 1; i >= 0; i--) { |
||
| 7071 | // undefined or not, key should match |
||
| 7072 | if (type[i].orig === callback || type[i].key === callback) { |
||
| 7073 | if (obj.removeEventListener) { |
||
| 7074 | obj.removeEventListener(name, type[i].func, false); |
||
| 7075 | } else if (obj.detachEvent) { |
||
| 7076 | obj.detachEvent('on'+name, type[i].func); |
||
| 7077 | } |
||
| 7078 | |||
| 7079 | type[i].orig = null; |
||
| 7080 | type[i].func = null; |
||
| 7081 | type.splice(i, 1); |
||
| 7082 | |||
| 7083 | // If callback was passed we are done here, otherwise proceed |
||
| 7084 | if (callback !== undef) { |
||
| 7085 | break; |
||
| 7086 | } |
||
| 7087 | } |
||
| 7088 | } |
||
| 7089 | |||
| 7090 | // If event array got empty, remove it |
||
| 7091 | if (!type.length) { |
||
| 7092 | delete eventhash[obj[uid]][name]; |
||
| 7093 | } |
||
| 7094 | |||
| 7095 | // If mOxie registry has become empty, remove it |
||
| 7096 | if (Basic.isEmptyObj(eventhash[obj[uid]])) { |
||
| 7097 | delete eventhash[obj[uid]]; |
||
| 7098 | |||
| 7099 | // IE doesn't let you remove DOM object property with - delete |
||
| 7100 | try { |
||
| 7101 | delete obj[uid]; |
||
| 7102 | } catch(e) { |
||
| 7103 | obj[uid] = undef; |
||
| 7104 | } |
||
| 7105 | } |
||
| 7106 | }; |
||
| 7107 | |||
| 7108 | |||
| 7109 | /** |
||
| 7110 | Remove all kind of events from the specified object |
||
| 7111 | |||
| 7112 | @method removeAllEvents |
||
| 7113 | @static |
||
| 7114 | @param {Object} obj DOM element to remove event listeners from. |
||
| 7115 | @param {String} [key] unique key to match, when removing events. |
||
| 7116 | */ |
||
| 7117 | var removeAllEvents = function(obj, key) { |
||
| 7118 | if (!obj || !obj[uid]) { |
||
| 7119 | return; |
||
| 7120 | } |
||
| 7121 | |||
| 7122 | Basic.each(eventhash[obj[uid]], function(events, name) { |
||
| 7123 | removeEvent(obj, name, key); |
||
| 7124 | }); |
||
| 7125 | }; |
||
| 7126 | |||
| 7127 | return { |
||
| 7128 | addEvent: addEvent, |
||
| 7129 | removeEvent: removeEvent, |
||
| 7130 | removeAllEvents: removeAllEvents |
||
| 7131 | }; |
||
| 7132 | }); |
||
| 7133 | |||
| 7134 | // Included from: src/javascript/runtime/html5/file/FileInput.js |
||
| 7135 | |||
| 7136 | /** |
||
| 7137 | * FileInput.js |
||
| 7138 | * |
||
| 7139 | * Copyright 2013, Moxiecode Systems AB |
||
| 7140 | * Released under GPL License. |
||
| 7141 | * |
||
| 7142 | * License: http://www.plupload.com/license |
||
| 7143 | * Contributing: http://www.plupload.com/contributing |
||
| 7144 | */ |
||
| 7145 | |||
| 7146 | /** |
||
| 7147 | @class moxie/runtime/html5/file/FileInput |
||
| 7148 | @private |
||
| 7149 | */ |
||
| 7150 | define("moxie/runtime/html5/file/FileInput", [ |
||
| 7151 | "moxie/runtime/html5/Runtime", |
||
| 7152 | "moxie/file/File", |
||
| 7153 | "moxie/core/utils/Basic", |
||
| 7154 | "moxie/core/utils/Dom", |
||
| 7155 | "moxie/core/utils/Events", |
||
| 7156 | "moxie/core/utils/Mime", |
||
| 7157 | "moxie/core/utils/Env" |
||
| 7158 | ], function(extensions, File, Basic, Dom, Events, Mime, Env) { |
||
| 7159 | |||
| 7160 | function FileInput() { |
||
| 7161 | var _options, _browseBtnZIndex; // save original z-index |
||
| 7162 | |||
| 7163 | Basic.extend(this, { |
||
| 7164 | init: function(options) { |
||
| 7165 | var comp = this, I = comp.getRuntime(), input, shimContainer, mimes, browseButton, zIndex, top; |
||
| 7166 | |||
| 7167 | _options = options; |
||
| 7168 | |||
| 7169 | // figure out accept string |
||
| 7170 | mimes = Mime.extList2mimes(_options.accept, I.can('filter_by_extension')); |
||
| 7171 | |||
| 7172 | shimContainer = I.getShimContainer(); |
||
| 7173 | |||
| 7174 | shimContainer.innerHTML = '<input id="' + I.uid +'" type="file" style="font-size:999px;opacity:0;"' + |
||
| 7175 | (_options.multiple && I.can('select_multiple') ? 'multiple' : '') + |
||
| 7176 | (_options.directory && I.can('select_folder') ? 'webkitdirectory directory' : '') + // Chrome 11+ |
||
| 7177 | (mimes ? ' accept="' + mimes.join(',') + '"' : '') + ' />'; |
||
| 7178 | |||
| 7179 | input = Dom.get(I.uid); |
||
| 7180 | |||
| 7181 | // prepare file input to be placed underneath the browse_button element |
||
| 7182 | Basic.extend(input.style, { |
||
| 7183 | position: 'absolute', |
||
| 7184 | top: 0, |
||
| 7185 | left: 0, |
||
| 7186 | width: '100%', |
||
| 7187 | height: '100%' |
||
| 7188 | }); |
||
| 7189 | |||
| 7190 | |||
| 7191 | browseButton = Dom.get(_options.browse_button); |
||
| 7192 | _browseBtnZIndex = Dom.getStyle(browseButton, 'z-index') || 'auto'; |
||
| 7193 | |||
| 7194 | // Route click event to the input[type=file] element for browsers that support such behavior |
||
| 7195 | if (I.can('summon_file_dialog')) { |
||
| 7196 | if (Dom.getStyle(browseButton, 'position') === 'static') { |
||
| 7197 | browseButton.style.position = 'relative'; |
||
| 7198 | } |
||
| 7199 | |||
| 7200 | Events.addEvent(browseButton, 'click', function(e) { |
||
| 7201 | var input = Dom.get(I.uid); |
||
| 7202 | if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file] |
||
| 7203 | input.click(); |
||
| 7204 | } |
||
| 7205 | e.preventDefault(); |
||
| 7206 | }, comp.uid); |
||
| 7207 | |||
| 7208 | comp.bind('Refresh', function() { |
||
| 7209 | zIndex = parseInt(_browseBtnZIndex, 10) || 1; |
||
| 7210 | |||
| 7211 | Dom.get(_options.browse_button).style.zIndex = zIndex; |
||
| 7212 | this.getRuntime().getShimContainer().style.zIndex = zIndex - 1; |
||
| 7213 | }); |
||
| 7214 | } |
||
| 7215 | |||
| 7216 | /* Since we have to place input[type=file] on top of the browse_button for some browsers, |
||
| 7217 | browse_button loses interactivity, so we restore it here */ |
||
| 7218 | top = I.can('summon_file_dialog') ? browseButton : shimContainer; |
||
| 7219 | |||
| 7220 | Events.addEvent(top, 'mouseover', function() { |
||
| 7221 | comp.trigger('mouseenter'); |
||
| 7222 | }, comp.uid); |
||
| 7223 | |||
| 7224 | Events.addEvent(top, 'mouseout', function() { |
||
| 7225 | comp.trigger('mouseleave'); |
||
| 7226 | }, comp.uid); |
||
| 7227 | |||
| 7228 | Events.addEvent(top, 'mousedown', function() { |
||
| 7229 | comp.trigger('mousedown'); |
||
| 7230 | }, comp.uid); |
||
| 7231 | |||
| 7232 | Events.addEvent(Dom.get(_options.container), 'mouseup', function() { |
||
| 7233 | comp.trigger('mouseup'); |
||
| 7234 | }, comp.uid); |
||
| 7235 | |||
| 7236 | // it shouldn't be possible to tab into the hidden element |
||
| 7237 | (I.can('summon_file_dialog') ? input : browseButton).setAttribute('tabindex', -1); |
||
| 7238 | |||
| 7239 | input.onchange = function onChange() { // there should be only one handler for this |
||
| 7240 | comp.files = []; |
||
| 7241 | |||
| 7242 | Basic.each(this.files, function(file) { |
||
| 7243 | var relativePath = ''; |
||
| 7244 | |||
| 7245 | if (_options.directory) { |
||
| 7246 | // folders are represented by dots, filter them out (Chrome 11+) |
||
| 7247 | if (file.name == ".") { |
||
| 7248 | // if it looks like a folder... |
||
| 7249 | return true; |
||
| 7250 | } |
||
| 7251 | } |
||
| 7252 | |||
| 7253 | if (file.webkitRelativePath) { |
||
| 7254 | relativePath = '/' + file.webkitRelativePath.replace(/^\//, ''); |
||
| 7255 | } |
||
| 7256 | |||
| 7257 | file = new File(I.uid, file); |
||
| 7258 | file.relativePath = relativePath; |
||
| 7259 | |||
| 7260 | comp.files.push(file); |
||
| 7261 | }); |
||
| 7262 | |||
| 7263 | // clearing the value enables the user to select the same file again if they want to |
||
| 7264 | if (Env.browser !== 'IE' && Env.browser !== 'IEMobile') { |
||
| 7265 | this.value = ''; |
||
| 7266 | } else { |
||
| 7267 | // in IE input[type="file"] is read-only so the only way to reset it is to re-insert it |
||
| 7268 | var clone = this.cloneNode(true); |
||
| 7269 | this.parentNode.replaceChild(clone, this); |
||
| 7270 | clone.onchange = onChange; |
||
| 7271 | } |
||
| 7272 | |||
| 7273 | if (comp.files.length) { |
||
| 7274 | comp.trigger('change'); |
||
| 7275 | } |
||
| 7276 | }; |
||
| 7277 | |||
| 7278 | // ready event is perfectly asynchronous |
||
| 7279 | comp.trigger({ |
||
| 7280 | type: 'ready', |
||
| 7281 | async: true |
||
| 7282 | }); |
||
| 7283 | |||
| 7284 | shimContainer = null; |
||
| 7285 | }, |
||
| 7286 | |||
| 7287 | |||
| 7288 | setOption: function(name, value) { |
||
| 7289 | var I = this.getRuntime(); |
||
| 7290 | var input = Dom.get(I.uid); |
||
| 7291 | |||
| 7292 | switch (name) { |
||
| 7293 | case 'accept': |
||
| 7294 | if (value) { |
||
| 7295 | var mimes = value.mimes || Mime.extList2mimes(value, I.can('filter_by_extension')); |
||
| 7296 | input.setAttribute('accept', mimes.join(',')); |
||
| 7297 | } else { |
||
| 7298 | input.removeAttribute('accept'); |
||
| 7299 | } |
||
| 7300 | break; |
||
| 7301 | |||
| 7302 | case 'directory': |
||
| 7303 | if (value && I.can('select_folder')) { |
||
| 7304 | input.setAttribute('directory', ''); |
||
| 7305 | input.setAttribute('webkitdirectory', ''); |
||
| 7306 | } else { |
||
| 7307 | input.removeAttribute('directory'); |
||
| 7308 | input.removeAttribute('webkitdirectory'); |
||
| 7309 | } |
||
| 7310 | break; |
||
| 7311 | |||
| 7312 | case 'multiple': |
||
| 7313 | if (value && I.can('select_multiple')) { |
||
| 7314 | input.setAttribute('multiple', ''); |
||
| 7315 | } else { |
||
| 7316 | input.removeAttribute('multiple'); |
||
| 7317 | } |
||
| 7318 | |||
| 7319 | } |
||
| 7320 | }, |
||
| 7321 | |||
| 7322 | |||
| 7323 | disable: function(state) { |
||
| 7324 | var I = this.getRuntime(), input; |
||
| 7325 | |||
| 7326 | if ((input = Dom.get(I.uid))) { |
||
| 7327 | input.disabled = !!state; |
||
| 7328 | } |
||
| 7329 | }, |
||
| 7330 | |||
| 7331 | destroy: function() { |
||
| 7332 | var I = this.getRuntime() |
||
| 7333 | , shim = I.getShim() |
||
| 7334 | , shimContainer = I.getShimContainer() |
||
| 7335 | , container = _options && Dom.get(_options.container) |
||
| 7336 | , browseButton = _options && Dom.get(_options.browse_button) |
||
| 7337 | ; |
||
| 7338 | |||
| 7339 | if (container) { |
||
| 7340 | Events.removeAllEvents(container, this.uid); |
||
| 7341 | } |
||
| 7342 | |||
| 7343 | if (browseButton) { |
||
| 7344 | Events.removeAllEvents(browseButton, this.uid); |
||
| 7345 | browseButton.style.zIndex = _browseBtnZIndex; // reset to original value |
||
| 7346 | } |
||
| 7347 | |||
| 7348 | if (shimContainer) { |
||
| 7349 | Events.removeAllEvents(shimContainer, this.uid); |
||
| 7350 | shimContainer.innerHTML = ''; |
||
| 7351 | } |
||
| 7352 | |||
| 7353 | shim.removeInstance(this.uid); |
||
| 7354 | |||
| 7355 | _options = shimContainer = container = browseButton = shim = null; |
||
| 7356 | } |
||
| 7357 | }); |
||
| 7358 | } |
||
| 7359 | |||
| 7360 | return (extensions.FileInput = FileInput); |
||
| 7361 | }); |
||
| 7362 | |||
| 7363 | // Included from: src/javascript/runtime/html5/file/FileDrop.js |
||
| 7364 | |||
| 7365 | /** |
||
| 7366 | * FileDrop.js |
||
| 7367 | * |
||
| 7368 | * Copyright 2013, Moxiecode Systems AB |
||
| 7369 | * Released under GPL License. |
||
| 7370 | * |
||
| 7371 | * License: http://www.plupload.com/license |
||
| 7372 | * Contributing: http://www.plupload.com/contributing |
||
| 7373 | */ |
||
| 7374 | |||
| 7375 | /** |
||
| 7376 | @class moxie/runtime/html5/file/FileDrop |
||
| 7377 | @private |
||
| 7378 | */ |
||
| 7379 | define("moxie/runtime/html5/file/FileDrop", [ |
||
| 7380 | "moxie/runtime/html5/Runtime", |
||
| 7381 | 'moxie/file/File', |
||
| 7382 | "moxie/core/utils/Basic", |
||
| 7383 | "moxie/core/utils/Dom", |
||
| 7384 | "moxie/core/utils/Events", |
||
| 7385 | "moxie/core/utils/Mime" |
||
| 7386 | ], function(extensions, File, Basic, Dom, Events, Mime) { |
||
| 7387 | |||
| 7388 | function FileDrop() { |
||
| 7389 | var _files = [], _allowedExts = [], _options, _ruid; |
||
| 7390 | |||
| 7391 | Basic.extend(this, { |
||
| 7392 | init: function(options) { |
||
| 7393 | var comp = this, dropZone; |
||
| 7394 | |||
| 7395 | _options = options; |
||
| 7396 | _ruid = comp.ruid; // every dropped-in file should have a reference to the runtime |
||
| 7397 | _allowedExts = _extractExts(_options.accept); |
||
| 7398 | dropZone = _options.container; |
||
| 7399 | |||
| 7400 | Events.addEvent(dropZone, 'dragover', function(e) { |
||
| 7401 | if (!_hasFiles(e)) { |
||
| 7402 | return; |
||
| 7403 | } |
||
| 7404 | e.preventDefault(); |
||
| 7405 | e.dataTransfer.dropEffect = 'copy'; |
||
| 7406 | }, comp.uid); |
||
| 7407 | |||
| 7408 | Events.addEvent(dropZone, 'drop', function(e) { |
||
| 7409 | if (!_hasFiles(e)) { |
||
| 7410 | return; |
||
| 7411 | } |
||
| 7412 | e.preventDefault(); |
||
| 7413 | |||
| 7414 | _files = []; |
||
| 7415 | |||
| 7416 | // Chrome 21+ accepts folders via Drag'n'Drop |
||
| 7417 | if (e.dataTransfer.items && e.dataTransfer.items[0].webkitGetAsEntry) { |
||
| 7418 | _readItems(e.dataTransfer.items, function() { |
||
| 7419 | comp.files = _files; |
||
| 7420 | comp.trigger("drop"); |
||
| 7421 | }); |
||
| 7422 | } else { |
||
| 7423 | Basic.each(e.dataTransfer.files, function(file) { |
||
| 7424 | _addFile(file); |
||
| 7425 | }); |
||
| 7426 | comp.files = _files; |
||
| 7427 | comp.trigger("drop"); |
||
| 7428 | } |
||
| 7429 | }, comp.uid); |
||
| 7430 | |||
| 7431 | Events.addEvent(dropZone, 'dragenter', function(e) { |
||
| 7432 | comp.trigger("dragenter"); |
||
| 7433 | }, comp.uid); |
||
| 7434 | |||
| 7435 | Events.addEvent(dropZone, 'dragleave', function(e) { |
||
| 7436 | comp.trigger("dragleave"); |
||
| 7437 | }, comp.uid); |
||
| 7438 | }, |
||
| 7439 | |||
| 7440 | destroy: function() { |
||
| 7441 | Events.removeAllEvents(_options && Dom.get(_options.container), this.uid); |
||
| 7442 | _ruid = _files = _allowedExts = _options = null; |
||
| 7443 | this.getRuntime().getShim().removeInstance(this.uid); |
||
| 7444 | } |
||
| 7445 | }); |
||
| 7446 | |||
| 7447 | |||
| 7448 | function _hasFiles(e) { |
||
| 7449 | if (!e.dataTransfer || !e.dataTransfer.types) { // e.dataTransfer.files is not available in Gecko during dragover |
||
| 7450 | return false; |
||
| 7451 | } |
||
| 7452 | |||
| 7453 | var types = Basic.toArray(e.dataTransfer.types || []); |
||
| 7454 | |||
| 7455 | return Basic.inArray("Files", types) !== -1 || |
||
| 7456 | Basic.inArray("public.file-url", types) !== -1 || // Safari < 5 |
||
| 7457 | Basic.inArray("application/x-moz-file", types) !== -1 // Gecko < 1.9.2 (< Firefox 3.6) |
||
| 7458 | ; |
||
| 7459 | } |
||
| 7460 | |||
| 7461 | |||
| 7462 | function _addFile(file, relativePath) { |
||
| 7463 | if (_isAcceptable(file)) { |
||
| 7464 | var fileObj = new File(_ruid, file); |
||
| 7465 | fileObj.relativePath = relativePath || ''; |
||
| 7466 | _files.push(fileObj); |
||
| 7467 | } |
||
| 7468 | } |
||
| 7469 | |||
| 7470 | |||
| 7471 | function _extractExts(accept) { |
||
| 7472 | var exts = []; |
||
| 7473 | for (var i = 0; i < accept.length; i++) { |
||
| 7474 | [].push.apply(exts, accept[i].extensions.split(/\s*,\s*/)); |
||
| 7475 | } |
||
| 7476 | return Basic.inArray('*', exts) === -1 ? exts : []; |
||
| 7477 | } |
||
| 7478 | |||
| 7479 | |||
| 7480 | function _isAcceptable(file) { |
||
| 7481 | if (!_allowedExts.length) { |
||
| 7482 | return true; |
||
| 7483 | } |
||
| 7484 | var ext = Mime.getFileExtension(file.name); |
||
| 7485 | return !ext || Basic.inArray(ext, _allowedExts) !== -1; |
||
| 7486 | } |
||
| 7487 | |||
| 7488 | |||
| 7489 | function _readItems(items, cb) { |
||
| 7490 | var entries = []; |
||
| 7491 | Basic.each(items, function(item) { |
||
| 7492 | var entry = item.webkitGetAsEntry(); |
||
| 7493 | // Address #998 (https://code.google.com/p/chromium/issues/detail?id=332579) |
||
| 7494 | if (entry) { |
||
| 7495 | // file() fails on OSX when the filename contains a special character (e.g. umlaut): see #61 |
||
| 7496 | if (entry.isFile) { |
||
| 7497 | _addFile(item.getAsFile(), entry.fullPath); |
||
| 7498 | } else { |
||
| 7499 | entries.push(entry); |
||
| 7500 | } |
||
| 7501 | } |
||
| 7502 | }); |
||
| 7503 | |||
| 7504 | if (entries.length) { |
||
| 7505 | _readEntries(entries, cb); |
||
| 7506 | } else { |
||
| 7507 | cb(); |
||
| 7508 | } |
||
| 7509 | } |
||
| 7510 | |||
| 7511 | |||
| 7512 | function _readEntries(entries, cb) { |
||
| 7513 | var queue = []; |
||
| 7514 | Basic.each(entries, function(entry) { |
||
| 7515 | queue.push(function(cbcb) { |
||
| 7516 | _readEntry(entry, cbcb); |
||
| 7517 | }); |
||
| 7518 | }); |
||
| 7519 | Basic.inSeries(queue, function() { |
||
| 7520 | cb(); |
||
| 7521 | }); |
||
| 7522 | } |
||
| 7523 | |||
| 7524 | |||
| 7525 | function _readEntry(entry, cb) { |
||
| 7526 | if (entry.isFile) { |
||
| 7527 | entry.file(function(file) { |
||
| 7528 | _addFile(file, entry.fullPath); |
||
| 7529 | cb(); |
||
| 7530 | }, function() { |
||
| 7531 | // fire an error event maybe |
||
| 7532 | cb(); |
||
| 7533 | }); |
||
| 7534 | } else if (entry.isDirectory) { |
||
| 7535 | _readDirEntry(entry, cb); |
||
| 7536 | } else { |
||
| 7537 | cb(); // not file, not directory? what then?.. |
||
| 7538 | } |
||
| 7539 | } |
||
| 7540 | |||
| 7541 | |||
| 7542 | function _readDirEntry(dirEntry, cb) { |
||
| 7543 | var entries = [], dirReader = dirEntry.createReader(); |
||
| 7544 | |||
| 7545 | // keep quering recursively till no more entries |
||
| 7546 | function getEntries(cbcb) { |
||
| 7547 | dirReader.readEntries(function(moreEntries) { |
||
| 7548 | if (moreEntries.length) { |
||
| 7549 | [].push.apply(entries, moreEntries); |
||
| 7550 | getEntries(cbcb); |
||
| 7551 | } else { |
||
| 7552 | cbcb(); |
||
| 7553 | } |
||
| 7554 | }, cbcb); |
||
| 7555 | } |
||
| 7556 | |||
| 7557 | // ...and you thought FileReader was crazy... |
||
| 7558 | getEntries(function() { |
||
| 7559 | _readEntries(entries, cb); |
||
| 7560 | }); |
||
| 7561 | } |
||
| 7562 | } |
||
| 7563 | |||
| 7564 | return (extensions.FileDrop = FileDrop); |
||
| 7565 | }); |
||
| 7566 | |||
| 7567 | // Included from: src/javascript/runtime/html5/file/FileReader.js |
||
| 7568 | |||
| 7569 | /** |
||
| 7570 | * FileReader.js |
||
| 7571 | * |
||
| 7572 | * Copyright 2013, Moxiecode Systems AB |
||
| 7573 | * Released under GPL License. |
||
| 7574 | * |
||
| 7575 | * License: http://www.plupload.com/license |
||
| 7576 | * Contributing: http://www.plupload.com/contributing |
||
| 7577 | */ |
||
| 7578 | |||
| 7579 | /** |
||
| 7580 | @class moxie/runtime/html5/file/FileReader |
||
| 7581 | @private |
||
| 7582 | */ |
||
| 7583 | define("moxie/runtime/html5/file/FileReader", [ |
||
| 7584 | "moxie/runtime/html5/Runtime", |
||
| 7585 | "moxie/core/utils/Encode", |
||
| 7586 | "moxie/core/utils/Basic" |
||
| 7587 | ], function(extensions, Encode, Basic) { |
||
| 7588 | |||
| 7589 | function FileReader() { |
||
| 7590 | var _fr, _convertToBinary = false; |
||
| 7591 | |||
| 7592 | Basic.extend(this, { |
||
| 7593 | |||
| 7594 | read: function(op, blob) { |
||
| 7595 | var comp = this; |
||
| 7596 | |||
| 7597 | comp.result = ''; |
||
| 7598 | |||
| 7599 | _fr = new window.FileReader(); |
||
| 7600 | |||
| 7601 | _fr.addEventListener('progress', function(e) { |
||
| 7602 | comp.trigger(e); |
||
| 7603 | }); |
||
| 7604 | |||
| 7605 | _fr.addEventListener('load', function(e) { |
||
| 7606 | comp.result = _convertToBinary ? _toBinary(_fr.result) : _fr.result; |
||
| 7607 | comp.trigger(e); |
||
| 7608 | }); |
||
| 7609 | |||
| 7610 | _fr.addEventListener('error', function(e) { |
||
| 7611 | comp.trigger(e, _fr.error); |
||
| 7612 | }); |
||
| 7613 | |||
| 7614 | _fr.addEventListener('loadend', function(e) { |
||
| 7615 | _fr = null; |
||
| 7616 | comp.trigger(e); |
||
| 7617 | }); |
||
| 7618 | |||
| 7619 | if (Basic.typeOf(_fr[op]) === 'function') { |
||
| 7620 | _convertToBinary = false; |
||
| 7621 | _fr[op](blob.getSource()); |
||
| 7622 | } else if (op === 'readAsBinaryString') { // readAsBinaryString is depricated in general and never existed in IE10+ |
||
| 7623 | _convertToBinary = true; |
||
| 7624 | _fr.readAsDataURL(blob.getSource()); |
||
| 7625 | } |
||
| 7626 | }, |
||
| 7627 | |||
| 7628 | abort: function() { |
||
| 7629 | if (_fr) { |
||
| 7630 | _fr.abort(); |
||
| 7631 | } |
||
| 7632 | }, |
||
| 7633 | |||
| 7634 | destroy: function() { |
||
| 7635 | _fr = null; |
||
| 7636 | this.getRuntime().getShim().removeInstance(this.uid); |
||
| 7637 | } |
||
| 7638 | }); |
||
| 7639 | |||
| 7640 | function _toBinary(str) { |
||
| 7641 | return Encode.atob(str.substring(str.indexOf('base64,') + 7)); |
||
| 7642 | } |
||
| 7643 | } |
||
| 7644 | |||
| 7645 | return (extensions.FileReader = FileReader); |
||
| 7646 | }); |
||
| 7647 | |||
| 7648 | // Included from: src/javascript/runtime/html5/xhr/XMLHttpRequest.js |
||
| 7649 | |||
| 7650 | /** |
||
| 7651 | * XMLHttpRequest.js |
||
| 7652 | * |
||
| 7653 | * Copyright 2013, Moxiecode Systems AB |
||
| 7654 | * Released under GPL License. |
||
| 7655 | * |
||
| 7656 | * License: http://www.plupload.com/license |
||
| 7657 | * Contributing: http://www.plupload.com/contributing |
||
| 7658 | */ |
||
| 7659 | |||
| 7660 | /*global ActiveXObject:true */ |
||
| 7661 | |||
| 7662 | /** |
||
| 7663 | @class moxie/runtime/html5/xhr/XMLHttpRequest |
||
| 7664 | @private |
||
| 7665 | */ |
||
| 7666 | define("moxie/runtime/html5/xhr/XMLHttpRequest", [ |
||
| 7667 | "moxie/runtime/html5/Runtime", |
||
| 7668 | "moxie/core/utils/Basic", |
||
| 7669 | "moxie/core/utils/Mime", |
||
| 7670 | "moxie/core/utils/Url", |
||
| 7671 | "moxie/file/File", |
||
| 7672 | "moxie/file/Blob", |
||
| 7673 | "moxie/xhr/FormData", |
||
| 7674 | "moxie/core/Exceptions", |
||
| 7675 | "moxie/core/utils/Env" |
||
| 7676 | ], function(extensions, Basic, Mime, Url, File, Blob, FormData, x, Env) { |
||
| 7677 | |||
| 7678 | function XMLHttpRequest() { |
||
| 7679 | var self = this |
||
| 7680 | , _xhr |
||
| 7681 | , _filename |
||
| 7682 | ; |
||
| 7683 | |||
| 7684 | Basic.extend(this, { |
||
| 7685 | send: function(meta, data) { |
||
| 7686 | var target = this |
||
| 7687 | , isGecko2_5_6 = (Env.browser === 'Mozilla' && Env.verComp(Env.version, 4, '>=') && Env.verComp(Env.version, 7, '<')) |
||
| 7688 | , isAndroidBrowser = Env.browser === 'Android Browser' |
||
| 7689 | , mustSendAsBinary = false |
||
| 7690 | ; |
||
| 7691 | |||
| 7692 | // extract file name |
||
| 7693 | _filename = meta.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase(); |
||
| 7694 | |||
| 7695 | _xhr = _getNativeXHR(); |
||
| 7696 | _xhr.open(meta.method, meta.url, meta.async, meta.user, meta.password); |
||
| 7697 | |||
| 7698 | |||
| 7699 | // prepare data to be sent |
||
| 7700 | if (data instanceof Blob) { |
||
| 7701 | if (data.isDetached()) { |
||
| 7702 | mustSendAsBinary = true; |
||
| 7703 | } |
||
| 7704 | data = data.getSource(); |
||
| 7705 | } else if (data instanceof FormData) { |
||
| 7706 | |||
| 7707 | if (data.hasBlob()) { |
||
| 7708 | if (data.getBlob().isDetached()) { |
||
| 7709 | data = _prepareMultipart.call(target, data); // _xhr must be instantiated and be in OPENED state |
||
| 7710 | mustSendAsBinary = true; |
||
| 7711 | } else if ((isGecko2_5_6 || isAndroidBrowser) && Basic.typeOf(data.getBlob().getSource()) === 'blob' && window.FileReader) { |
||
| 7712 | // Gecko 2/5/6 can't send blob in FormData: https://bugzilla.mozilla.org/show_bug.cgi?id=649150 |
||
| 7713 | // Android browsers (default one and Dolphin) seem to have the same issue, see: #613 |
||
| 7714 | _preloadAndSend.call(target, meta, data); |
||
| 7715 | return; // _preloadAndSend will reinvoke send() with transmutated FormData =%D |
||
| 7716 | } |
||
| 7717 | } |
||
| 7718 | |||
| 7719 | // transfer fields to real FormData |
||
| 7720 | if (data instanceof FormData) { // if still a FormData, e.g. not mangled by _prepareMultipart() |
||
| 7721 | var fd = new window.FormData(); |
||
| 7722 | data.each(function(value, name) { |
||
| 7723 | if (value instanceof Blob) { |
||
| 7724 | fd.append(name, value.getSource()); |
||
| 7725 | } else { |
||
| 7726 | fd.append(name, value); |
||
| 7727 | } |
||
| 7728 | }); |
||
| 7729 | data = fd; |
||
| 7730 | } |
||
| 7731 | } |
||
| 7732 | |||
| 7733 | |||
| 7734 | // if XHR L2 |
||
| 7735 | if (_xhr.upload) { |
||
| 7736 | if (meta.withCredentials) { |
||
| 7737 | _xhr.withCredentials = true; |
||
| 7738 | } |
||
| 7739 | |||
| 7740 | _xhr.addEventListener('load', function(e) { |
||
| 7741 | target.trigger(e); |
||
| 7742 | }); |
||
| 7743 | |||
| 7744 | _xhr.addEventListener('error', function(e) { |
||
| 7745 | target.trigger(e); |
||
| 7746 | }); |
||
| 7747 | |||
| 7748 | // additionally listen to progress events |
||
| 7749 | _xhr.addEventListener('progress', function(e) { |
||
| 7750 | target.trigger(e); |
||
| 7751 | }); |
||
| 7752 | |||
| 7753 | _xhr.upload.addEventListener('progress', function(e) { |
||
| 7754 | target.trigger({ |
||
| 7755 | type: 'UploadProgress', |
||
| 7756 | loaded: e.loaded, |
||
| 7757 | total: e.total |
||
| 7758 | }); |
||
| 7759 | }); |
||
| 7760 | // ... otherwise simulate XHR L2 |
||
| 7761 | } else { |
||
| 7762 | _xhr.onreadystatechange = function onReadyStateChange() { |
||
| 7763 | |||
| 7764 | // fake Level 2 events |
||
| 7765 | switch (_xhr.readyState) { |
||
| 7766 | |||
| 7767 | case 1: // XMLHttpRequest.OPENED |
||
| 7768 | // readystatechanged is fired twice for OPENED state (in IE and Mozilla) - neu |
||
| 7769 | break; |
||
| 7770 | |||
| 7771 | // looks like HEADERS_RECEIVED (state 2) is not reported in Opera (or it's old versions) - neu |
||
| 7772 | case 2: // XMLHttpRequest.HEADERS_RECEIVED |
||
| 7773 | break; |
||
| 7774 | |||
| 7775 | case 3: // XMLHttpRequest.LOADING |
||
| 7776 | // try to fire progress event for not XHR L2 |
||
| 7777 | var total, loaded; |
||
| 7778 | |||
| 7779 | try { |
||
| 7780 | if (Url.hasSameOrigin(meta.url)) { // Content-Length not accessible for cross-domain on some browsers |
||
| 7781 | total = _xhr.getResponseHeader('Content-Length') || 0; // old Safari throws an exception here |
||
| 7782 | } |
||
| 7783 | |||
| 7784 | if (_xhr.responseText) { // responseText was introduced in IE7 |
||
| 7785 | loaded = _xhr.responseText.length; |
||
| 7786 | } |
||
| 7787 | } catch(ex) { |
||
| 7788 | total = loaded = 0; |
||
| 7789 | } |
||
| 7790 | |||
| 7791 | target.trigger({ |
||
| 7792 | type: 'progress', |
||
| 7793 | lengthComputable: !!total, |
||
| 7794 | total: parseInt(total, 10), |
||
| 7795 | loaded: loaded |
||
| 7796 | }); |
||
| 7797 | break; |
||
| 7798 | |||
| 7799 | case 4: // XMLHttpRequest.DONE |
||
| 7800 | // release readystatechange handler (mostly for IE) |
||
| 7801 | _xhr.onreadystatechange = function() {}; |
||
| 7802 | |||
| 7803 | // usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout |
||
| 7804 | try { |
||
| 7805 | if (_xhr.status >= 200 && _xhr.status < 400) { |
||
| 7806 | target.trigger('load'); |
||
| 7807 | break; |
||
| 7808 | } |
||
| 7809 | } catch(ex) {} |
||
| 7810 | |||
| 7811 | target.trigger('error'); |
||
| 7812 | break; |
||
| 7813 | } |
||
| 7814 | }; |
||
| 7815 | } |
||
| 7816 | |||
| 7817 | |||
| 7818 | // set request headers |
||
| 7819 | if (!Basic.isEmptyObj(meta.headers)) { |
||
| 7820 | Basic.each(meta.headers, function(value, header) { |
||
| 7821 | _xhr.setRequestHeader(header, value); |
||
| 7822 | }); |
||
| 7823 | } |
||
| 7824 | |||
| 7825 | // request response type |
||
| 7826 | if ("" !== meta.responseType && 'responseType' in _xhr) { |
||
| 7827 | if ('json' === meta.responseType && !Env.can('return_response_type', 'json')) { // we can fake this one |
||
| 7828 | _xhr.responseType = 'text'; |
||
| 7829 | } else { |
||
| 7830 | _xhr.responseType = meta.responseType; |
||
| 7831 | } |
||
| 7832 | } |
||
| 7833 | |||
| 7834 | // send ... |
||
| 7835 | if (!mustSendAsBinary) { |
||
| 7836 | _xhr.send(data); |
||
| 7837 | } else { |
||
| 7838 | if (_xhr.sendAsBinary) { // Gecko |
||
| 7839 | _xhr.sendAsBinary(data); |
||
| 7840 | } else { // other browsers having support for typed arrays |
||
| 7841 | (function() { |
||
| 7842 | // mimic Gecko's sendAsBinary |
||
| 7843 | var ui8a = new Uint8Array(data.length); |
||
| 7844 | for (var i = 0; i < data.length; i++) { |
||
| 7845 | ui8a[i] = (data.charCodeAt(i) & 0xff); |
||
| 7846 | } |
||
| 7847 | _xhr.send(ui8a.buffer); |
||
| 7848 | }()); |
||
| 7849 | } |
||
| 7850 | } |
||
| 7851 | |||
| 7852 | target.trigger('loadstart'); |
||
| 7853 | }, |
||
| 7854 | |||
| 7855 | getStatus: function() { |
||
| 7856 | // according to W3C spec it should return 0 for readyState < 3, but instead it throws an exception |
||
| 7857 | try { |
||
| 7858 | if (_xhr) { |
||
| 7859 | return _xhr.status; |
||
| 7860 | } |
||
| 7861 | } catch(ex) {} |
||
| 7862 | return 0; |
||
| 7863 | }, |
||
| 7864 | |||
| 7865 | getResponse: function(responseType) { |
||
| 7866 | var I = this.getRuntime(); |
||
| 7867 | |||
| 7868 | try { |
||
| 7869 | switch (responseType) { |
||
| 7870 | case 'blob': |
||
| 7871 | var file = new File(I.uid, _xhr.response); |
||
| 7872 | |||
| 7873 | // try to extract file name from content-disposition if possible (might be - not, if CORS for example) |
||
| 7874 | var disposition = _xhr.getResponseHeader('Content-Disposition'); |
||
| 7875 | if (disposition) { |
||
| 7876 | // extract filename from response header if available |
||
| 7877 | var match = disposition.match(/filename=([\'\"'])([^\1]+)\1/); |
||
| 7878 | if (match) { |
||
| 7879 | _filename = match[2]; |
||
| 7880 | } |
||
| 7881 | } |
||
| 7882 | file.name = _filename; |
||
| 7883 | |||
| 7884 | // pre-webkit Opera doesn't set type property on the blob response |
||
| 7885 | if (!file.type) { |
||
| 7886 | file.type = Mime.getFileMime(_filename); |
||
| 7887 | } |
||
| 7888 | return file; |
||
| 7889 | |||
| 7890 | case 'json': |
||
| 7891 | if (!Env.can('return_response_type', 'json')) { |
||
| 7892 | return _xhr.status === 200 && !!window.JSON ? JSON.parse(_xhr.responseText) : null; |
||
| 7893 | } |
||
| 7894 | return _xhr.response; |
||
| 7895 | |||
| 7896 | case 'document': |
||
| 7897 | return _getDocument(_xhr); |
||
| 7898 | |||
| 7899 | default: |
||
| 7900 | return _xhr.responseText !== '' ? _xhr.responseText : null; // against the specs, but for consistency across the runtimes |
||
| 7901 | } |
||
| 7902 | } catch(ex) { |
||
| 7903 | return null; |
||
| 7904 | } |
||
| 7905 | }, |
||
| 7906 | |||
| 7907 | getAllResponseHeaders: function() { |
||
| 7908 | try { |
||
| 7909 | return _xhr.getAllResponseHeaders(); |
||
| 7910 | } catch(ex) {} |
||
| 7911 | return ''; |
||
| 7912 | }, |
||
| 7913 | |||
| 7914 | abort: function() { |
||
| 7915 | if (_xhr) { |
||
| 7916 | _xhr.abort(); |
||
| 7917 | } |
||
| 7918 | }, |
||
| 7919 | |||
| 7920 | destroy: function() { |
||
| 7921 | self = _filename = null; |
||
| 7922 | this.getRuntime().getShim().removeInstance(this.uid); |
||
| 7923 | } |
||
| 7924 | }); |
||
| 7925 | |||
| 7926 | |||
| 7927 | // here we go... ugly fix for ugly bug |
||
| 7928 | function _preloadAndSend(meta, data) { |
||
| 7929 | var target = this, blob, fr; |
||
| 7930 | |||
| 7931 | // get original blob |
||
| 7932 | blob = data.getBlob().getSource(); |
||
| 7933 | |||
| 7934 | // preload blob in memory to be sent as binary string |
||
| 7935 | fr = new window.FileReader(); |
||
| 7936 | fr.onload = function() { |
||
| 7937 | // overwrite original blob |
||
| 7938 | data.append(data.getBlobName(), new Blob(null, { |
||
| 7939 | type: blob.type, |
||
| 7940 | data: fr.result |
||
| 7941 | })); |
||
| 7942 | // invoke send operation again |
||
| 7943 | self.send.call(target, meta, data); |
||
| 7944 | }; |
||
| 7945 | fr.readAsBinaryString(blob); |
||
| 7946 | } |
||
| 7947 | |||
| 7948 | |||
| 7949 | function _getNativeXHR() { |
||
| 7950 | if (window.XMLHttpRequest && !(Env.browser === 'IE' && Env.verComp(Env.version, 8, '<'))) { // IE7 has native XHR but it's buggy |
||
| 7951 | return new window.XMLHttpRequest(); |
||
| 7952 | } else { |
||
| 7953 | return (function() { |
||
| 7954 | var progIDs = ['Msxml2.XMLHTTP.6.0', 'Microsoft.XMLHTTP']; // if 6.0 available, use it, otherwise failback to default 3.0 |
||
| 7955 | for (var i = 0; i < progIDs.length; i++) { |
||
| 7956 | try { |
||
| 7957 | return new ActiveXObject(progIDs[i]); |
||
| 7958 | } catch (ex) {} |
||
| 7959 | } |
||
| 7960 | })(); |
||
| 7961 | } |
||
| 7962 | } |
||
| 7963 | |||
| 7964 | // @credits Sergey Ilinsky (http://www.ilinsky.com/) |
||
| 7965 | function _getDocument(xhr) { |
||
| 7966 | var rXML = xhr.responseXML; |
||
| 7967 | var rText = xhr.responseText; |
||
| 7968 | |||
| 7969 | // Try parsing responseText (@see: http://www.ilinsky.com/articles/XMLHttpRequest/#bugs-ie-responseXML-content-type) |
||
| 7970 | if (Env.browser === 'IE' && rText && rXML && !rXML.documentElement && /[^\/]+\/[^\+]+\+xml/.test(xhr.getResponseHeader("Content-Type"))) { |
||
| 7971 | rXML = new window.ActiveXObject("Microsoft.XMLDOM"); |
||
| 7972 | rXML.async = false; |
||
| 7973 | rXML.validateOnParse = false; |
||
| 7974 | rXML.loadXML(rText); |
||
| 7975 | } |
||
| 7976 | |||
| 7977 | // Check if there is no error in document |
||
| 7978 | if (rXML) { |
||
| 7979 | if ((Env.browser === 'IE' && rXML.parseError !== 0) || !rXML.documentElement || rXML.documentElement.tagName === "parsererror") { |
||
| 7980 | return null; |
||
| 7981 | } |
||
| 7982 | } |
||
| 7983 | return rXML; |
||
| 7984 | } |
||
| 7985 | |||
| 7986 | |||
| 7987 | function _prepareMultipart(fd) { |
||
| 7988 | var boundary = '----moxieboundary' + new Date().getTime() |
||
| 7989 | , dashdash = '--' |
||
| 7990 | , crlf = '\r\n' |
||
| 7991 | , multipart = '' |
||
| 7992 | , I = this.getRuntime() |
||
| 7993 | ; |
||
| 7994 | |||
| 7995 | if (!I.can('send_binary_string')) { |
||
| 7996 | throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR); |
||
| 7997 | } |
||
| 7998 | |||
| 7999 | _xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); |
||
| 8000 | |||
| 8001 | // append multipart parameters |
||
| 8002 | fd.each(function(value, name) { |
||
| 8003 | // Firefox 3.6 failed to convert multibyte characters to UTF-8 in sendAsBinary(), |
||
| 8004 | // so we try it here ourselves with: unescape(encodeURIComponent(value)) |
||
| 8005 | if (value instanceof Blob) { |
||
| 8006 | // Build RFC2388 blob |
||
| 8007 | multipart += dashdash + boundary + crlf + |
||
| 8008 | 'Content-Disposition: form-data; name="' + name + '"; filename="' + unescape(encodeURIComponent(value.name || 'blob')) + '"' + crlf + |
||
| 8009 | 'Content-Type: ' + (value.type || 'application/octet-stream') + crlf + crlf + |
||
| 8010 | value.getSource() + crlf; |
||
| 8011 | } else { |
||
| 8012 | multipart += dashdash + boundary + crlf + |
||
| 8013 | 'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf + |
||
| 8014 | unescape(encodeURIComponent(value)) + crlf; |
||
| 8015 | } |
||
| 8016 | }); |
||
| 8017 | |||
| 8018 | multipart += dashdash + boundary + dashdash + crlf; |
||
| 8019 | |||
| 8020 | return multipart; |
||
| 8021 | } |
||
| 8022 | } |
||
| 8023 | |||
| 8024 | return (extensions.XMLHttpRequest = XMLHttpRequest); |
||
| 8025 | }); |
||
| 8026 | |||
| 8027 | // Included from: src/javascript/runtime/html5/utils/BinaryReader.js |
||
| 8028 | |||
| 8029 | /** |
||
| 8030 | * BinaryReader.js |
||
| 8031 | * |
||
| 8032 | * Copyright 2013, Moxiecode Systems AB |
||
| 8033 | * Released under GPL License. |
||
| 8034 | * |
||
| 8035 | * License: http://www.plupload.com/license |
||
| 8036 | * Contributing: http://www.plupload.com/contributing |
||
| 8037 | */ |
||
| 8038 | |||
| 8039 | /** |
||
| 8040 | @class moxie/runtime/html5/utils/BinaryReader |
||
| 8041 | @private |
||
| 8042 | */ |
||
| 8043 | define("moxie/runtime/html5/utils/BinaryReader", [ |
||
| 8044 | "moxie/core/utils/Basic" |
||
| 8045 | ], function(Basic) { |
||
| 8046 | |||
| 8047 | |||
| 8048 | function BinaryReader(data) { |
||
| 8049 | if (data instanceof ArrayBuffer) { |
||
| 8050 | ArrayBufferReader.apply(this, arguments); |
||
| 8051 | } else { |
||
| 8052 | UTF16StringReader.apply(this, arguments); |
||
| 8053 | } |
||
| 8054 | } |
||
| 8055 | |||
| 8056 | Basic.extend(BinaryReader.prototype, { |
||
| 8057 | |||
| 8058 | littleEndian: false, |
||
| 8059 | |||
| 8060 | |||
| 8061 | read: function(idx, size) { |
||
| 8062 | var sum, mv, i; |
||
| 8063 | |||
| 8064 | if (idx + size > this.length()) { |
||
| 8065 | throw new Error("You are trying to read outside the source boundaries."); |
||
| 8066 | } |
||
| 8067 | |||
| 8068 | mv = this.littleEndian |
||
| 8069 | ? 0 |
||
| 8070 | : -8 * (size - 1) |
||
| 8071 | ; |
||
| 8072 | |||
| 8073 | for (i = 0, sum = 0; i < size; i++) { |
||
| 8074 | sum |= (this.readByteAt(idx + i) << Math.abs(mv + i*8)); |
||
| 8075 | } |
||
| 8076 | return sum; |
||
| 8077 | }, |
||
| 8078 | |||
| 8079 | |||
| 8080 | write: function(idx, num, size) { |
||
| 8081 | var mv, i, str = ''; |
||
| 8082 | |||
| 8083 | if (idx > this.length()) { |
||
| 8084 | throw new Error("You are trying to write outside the source boundaries."); |
||
| 8085 | } |
||
| 8086 | |||
| 8087 | mv = this.littleEndian |
||
| 8088 | ? 0 |
||
| 8089 | : -8 * (size - 1) |
||
| 8090 | ; |
||
| 8091 | |||
| 8092 | for (i = 0; i < size; i++) { |
||
| 8093 | this.writeByteAt(idx + i, (num >> Math.abs(mv + i*8)) & 255); |
||
| 8094 | } |
||
| 8095 | }, |
||
| 8096 | |||
| 8097 | |||
| 8098 | BYTE: function(idx) { |
||
| 8099 | return this.read(idx, 1); |
||
| 8100 | }, |
||
| 8101 | |||
| 8102 | |||
| 8103 | SHORT: function(idx) { |
||
| 8104 | return this.read(idx, 2); |
||
| 8105 | }, |
||
| 8106 | |||
| 8107 | |||
| 8108 | LONG: function(idx) { |
||
| 8109 | return this.read(idx, 4); |
||
| 8110 | }, |
||
| 8111 | |||
| 8112 | |||
| 8113 | SLONG: function(idx) { // 2's complement notation |
||
| 8114 | var num = this.read(idx, 4); |
||
| 8115 | return (num > 2147483647 ? num - 4294967296 : num); |
||
| 8116 | }, |
||
| 8117 | |||
| 8118 | |||
| 8119 | CHAR: function(idx) { |
||
| 8120 | return String.fromCharCode(this.read(idx, 1)); |
||
| 8121 | }, |
||
| 8122 | |||
| 8123 | |||
| 8124 | STRING: function(idx, count) { |
||
| 8125 | return this.asArray('CHAR', idx, count).join(''); |
||
| 8126 | }, |
||
| 8127 | |||
| 8128 | |||
| 8129 | asArray: function(type, idx, count) { |
||
| 8130 | var values = []; |
||
| 8131 | |||
| 8132 | for (var i = 0; i < count; i++) { |
||
| 8133 | values[i] = this[type](idx + i); |
||
| 8134 | } |
||
| 8135 | return values; |
||
| 8136 | } |
||
| 8137 | }); |
||
| 8138 | |||
| 8139 | |||
| 8140 | function ArrayBufferReader(data) { |
||
| 8141 | var _dv = new DataView(data); |
||
| 8142 | |||
| 8143 | Basic.extend(this, { |
||
| 8144 | |||
| 8145 | readByteAt: function(idx) { |
||
| 8146 | return _dv.getUint8(idx); |
||
| 8147 | }, |
||
| 8148 | |||
| 8149 | |||
| 8150 | writeByteAt: function(idx, value) { |
||
| 8151 | _dv.setUint8(idx, value); |
||
| 8152 | }, |
||
| 8153 | |||
| 8154 | |||
| 8155 | SEGMENT: function(idx, size, value) { |
||
| 8156 | switch (arguments.length) { |
||
| 8157 | case 2: |
||
| 8158 | return data.slice(idx, idx + size); |
||
| 8159 | |||
| 8160 | case 1: |
||
| 8161 | return data.slice(idx); |
||
| 8162 | |||
| 8163 | case 3: |
||
| 8164 | if (value === null) { |
||
| 8165 | value = new ArrayBuffer(); |
||
| 8166 | } |
||
| 8167 | |||
| 8168 | if (value instanceof ArrayBuffer) { |
||
| 8169 | var arr = new Uint8Array(this.length() - size + value.byteLength); |
||
| 8170 | if (idx > 0) { |
||
| 8171 | arr.set(new Uint8Array(data.slice(0, idx)), 0); |
||
| 8172 | } |
||
| 8173 | arr.set(new Uint8Array(value), idx); |
||
| 8174 | arr.set(new Uint8Array(data.slice(idx + size)), idx + value.byteLength); |
||
| 8175 | |||
| 8176 | this.clear(); |
||
| 8177 | data = arr.buffer; |
||
| 8178 | _dv = new DataView(data); |
||
| 8179 | break; |
||
| 8180 | } |
||
| 8181 | |||
| 8182 | default: return data; |
||
| 8183 | } |
||
| 8184 | }, |
||
| 8185 | |||
| 8186 | |||
| 8187 | length: function() { |
||
| 8188 | return data ? data.byteLength : 0; |
||
| 8189 | }, |
||
| 8190 | |||
| 8191 | |||
| 8192 | clear: function() { |
||
| 8193 | _dv = data = null; |
||
| 8194 | } |
||
| 8195 | }); |
||
| 8196 | } |
||
| 8197 | |||
| 8198 | |||
| 8199 | function UTF16StringReader(data) { |
||
| 8200 | Basic.extend(this, { |
||
| 8201 | |||
| 8202 | readByteAt: function(idx) { |
||
| 8203 | return data.charCodeAt(idx); |
||
| 8204 | }, |
||
| 8205 | |||
| 8206 | |||
| 8207 | writeByteAt: function(idx, value) { |
||
| 8208 | putstr(String.fromCharCode(value), idx, 1); |
||
| 8209 | }, |
||
| 8210 | |||
| 8211 | |||
| 8212 | SEGMENT: function(idx, length, segment) { |
||
| 8213 | switch (arguments.length) { |
||
| 8214 | case 1: |
||
| 8215 | return data.substr(idx); |
||
| 8216 | case 2: |
||
| 8217 | return data.substr(idx, length); |
||
| 8218 | case 3: |
||
| 8219 | putstr(segment !== null ? segment : '', idx, length); |
||
| 8220 | break; |
||
| 8221 | default: return data; |
||
| 8222 | } |
||
| 8223 | }, |
||
| 8224 | |||
| 8225 | |||
| 8226 | length: function() { |
||
| 8227 | return data ? data.length : 0; |
||
| 8228 | }, |
||
| 8229 | |||
| 8230 | clear: function() { |
||
| 8231 | data = null; |
||
| 8232 | } |
||
| 8233 | }); |
||
| 8234 | |||
| 8235 | |||
| 8236 | function putstr(segment, idx, length) { |
||
| 8237 | length = arguments.length === 3 ? length : data.length - idx - 1; |
||
| 8238 | data = data.substr(0, idx) + segment + data.substr(length + idx); |
||
| 8239 | } |
||
| 8240 | } |
||
| 8241 | |||
| 8242 | |||
| 8243 | return BinaryReader; |
||
| 8244 | }); |
||
| 8245 | |||
| 8246 | // Included from: src/javascript/runtime/html5/image/JPEGHeaders.js |
||
| 8247 | |||
| 8248 | /** |
||
| 8249 | * JPEGHeaders.js |
||
| 8250 | * |
||
| 8251 | * Copyright 2013, Moxiecode Systems AB |
||
| 8252 | * Released under GPL License. |
||
| 8253 | * |
||
| 8254 | * License: http://www.plupload.com/license |
||
| 8255 | * Contributing: http://www.plupload.com/contributing |
||
| 8256 | */ |
||
| 8257 | |||
| 8258 | /** |
||
| 8259 | @class moxie/runtime/html5/image/JPEGHeaders |
||
| 8260 | @private |
||
| 8261 | */ |
||
| 8262 | define("moxie/runtime/html5/image/JPEGHeaders", [ |
||
| 8263 | "moxie/runtime/html5/utils/BinaryReader", |
||
| 8264 | "moxie/core/Exceptions" |
||
| 8265 | ], function(BinaryReader, x) { |
||
| 8266 | |||
| 8267 | return function JPEGHeaders(data) { |
||
| 8268 | var headers = [], _br, idx, marker, length = 0; |
||
| 8269 | |||
| 8270 | _br = new BinaryReader(data); |
||
| 8271 | |||
| 8272 | // Check if data is jpeg |
||
| 8273 | if (_br.SHORT(0) !== 0xFFD8) { |
||
| 8274 | _br.clear(); |
||
| 8275 | throw new x.ImageError(x.ImageError.WRONG_FORMAT); |
||
| 8276 | } |
||
| 8277 | |||
| 8278 | idx = 2; |
||
| 8279 | |||
| 8280 | while (idx <= _br.length()) { |
||
| 8281 | marker = _br.SHORT(idx); |
||
| 8282 | |||
| 8283 | // omit RST (restart) markers |
||
| 8284 | if (marker >= 0xFFD0 && marker <= 0xFFD7) { |
||
| 8285 | idx += 2; |
||
| 8286 | continue; |
||
| 8287 | } |
||
| 8288 | |||
| 8289 | // no headers allowed after SOS marker |
||
| 8290 | if (marker === 0xFFDA || marker === 0xFFD9) { |
||
| 8291 | break; |
||
| 8292 | } |
||
| 8293 | |||
| 8294 | length = _br.SHORT(idx + 2) + 2; |
||
| 8295 | |||
| 8296 | // APPn marker detected |
||
| 8297 | if (marker >= 0xFFE1 && marker <= 0xFFEF) { |
||
| 8298 | headers.push({ |
||
| 8299 | hex: marker, |
||
| 8300 | name: 'APP' + (marker & 0x000F), |
||
| 8301 | start: idx, |
||
| 8302 | length: length, |
||
| 8303 | segment: _br.SEGMENT(idx, length) |
||
| 8304 | }); |
||
| 8305 | } |
||
| 8306 | |||
| 8307 | idx += length; |
||
| 8308 | } |
||
| 8309 | |||
| 8310 | _br.clear(); |
||
| 8311 | |||
| 8312 | return { |
||
| 8313 | headers: headers, |
||
| 8314 | |||
| 8315 | restore: function(data) { |
||
| 8316 | var max, i, br; |
||
| 8317 | |||
| 8318 | br = new BinaryReader(data); |
||
| 8319 | |||
| 8320 | idx = br.SHORT(2) == 0xFFE0 ? 4 + br.SHORT(4) : 2; |
||
| 8321 | |||
| 8322 | for (i = 0, max = headers.length; i < max; i++) { |
||
| 8323 | br.SEGMENT(idx, 0, headers[i].segment); |
||
| 8324 | idx += headers[i].length; |
||
| 8325 | } |
||
| 8326 | |||
| 8327 | data = br.SEGMENT(); |
||
| 8328 | br.clear(); |
||
| 8329 | return data; |
||
| 8330 | }, |
||
| 8331 | |||
| 8332 | strip: function(data) { |
||
| 8333 | var br, headers, jpegHeaders, i; |
||
| 8334 | |||
| 8335 | jpegHeaders = new JPEGHeaders(data); |
||
| 8336 | headers = jpegHeaders.headers; |
||
| 8337 | jpegHeaders.purge(); |
||
| 8338 | |||
| 8339 | br = new BinaryReader(data); |
||
| 8340 | |||
| 8341 | i = headers.length; |
||
| 8342 | while (i--) { |
||
| 8343 | br.SEGMENT(headers[i].start, headers[i].length, ''); |
||
| 8344 | } |
||
| 8345 | |||
| 8346 | data = br.SEGMENT(); |
||
| 8347 | br.clear(); |
||
| 8348 | return data; |
||
| 8349 | }, |
||
| 8350 | |||
| 8351 | get: function(name) { |
||
| 8352 | var array = []; |
||
| 8353 | |||
| 8354 | for (var i = 0, max = headers.length; i < max; i++) { |
||
| 8355 | if (headers[i].name === name.toUpperCase()) { |
||
| 8356 | array.push(headers[i].segment); |
||
| 8357 | } |
||
| 8358 | } |
||
| 8359 | return array; |
||
| 8360 | }, |
||
| 8361 | |||
| 8362 | set: function(name, segment) { |
||
| 8363 | var array = [], i, ii, max; |
||
| 8364 | |||
| 8365 | if (typeof(segment) === 'string') { |
||
| 8366 | array.push(segment); |
||
| 8367 | } else { |
||
| 8368 | array = segment; |
||
| 8369 | } |
||
| 8370 | |||
| 8371 | for (i = ii = 0, max = headers.length; i < max; i++) { |
||
| 8372 | if (headers[i].name === name.toUpperCase()) { |
||
| 8373 | headers[i].segment = array[ii]; |
||
| 8374 | headers[i].length = array[ii].length; |
||
| 8375 | ii++; |
||
| 8376 | } |
||
| 8377 | if (ii >= array.length) { |
||
| 8378 | break; |
||
| 8379 | } |
||
| 8380 | } |
||
| 8381 | }, |
||
| 8382 | |||
| 8383 | purge: function() { |
||
| 8384 | this.headers = headers = []; |
||
| 8385 | } |
||
| 8386 | }; |
||
| 8387 | }; |
||
| 8388 | }); |
||
| 8389 | |||
| 8390 | // Included from: src/javascript/runtime/html5/image/ExifParser.js |
||
| 8391 | |||
| 8392 | /** |
||
| 8393 | * ExifParser.js |
||
| 8394 | * |
||
| 8395 | * Copyright 2013, Moxiecode Systems AB |
||
| 8396 | * Released under GPL License. |
||
| 8397 | * |
||
| 8398 | * License: http://www.plupload.com/license |
||
| 8399 | * Contributing: http://www.plupload.com/contributing |
||
| 8400 | */ |
||
| 8401 | |||
| 8402 | /** |
||
| 8403 | @class moxie/runtime/html5/image/ExifParser |
||
| 8404 | @private |
||
| 8405 | */ |
||
| 8406 | define("moxie/runtime/html5/image/ExifParser", [ |
||
| 8407 | "moxie/core/utils/Basic", |
||
| 8408 | "moxie/runtime/html5/utils/BinaryReader", |
||
| 8409 | "moxie/core/Exceptions" |
||
| 8410 | ], function(Basic, BinaryReader, x) { |
||
| 8411 | |||
| 8412 | function ExifParser(data) { |
||
| 8413 | var __super__, tags, tagDescs, offsets, idx, Tiff; |
||
| 8414 | |||
| 8415 | BinaryReader.call(this, data); |
||
| 8416 | |||
| 8417 | tags = { |
||
| 8418 | tiff: { |
||
| 8419 | /* |
||
| 8420 | The image orientation viewed in terms of rows and columns. |
||
| 8421 | |||
| 8422 | 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. |
||
| 8423 | 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. |
||
| 8424 | 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side. |
||
| 8425 | 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. |
||
| 8426 | 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. |
||
| 8427 | 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. |
||
| 8428 | 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. |
||
| 8429 | 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. |
||
| 8430 | */ |
||
| 8431 | 0x0112: 'Orientation', |
||
| 8432 | 0x010E: 'ImageDescription', |
||
| 8433 | 0x010F: 'Make', |
||
| 8434 | 0x0110: 'Model', |
||
| 8435 | 0x0131: 'Software', |
||
| 8436 | 0x8769: 'ExifIFDPointer', |
||
| 8437 | 0x8825: 'GPSInfoIFDPointer' |
||
| 8438 | }, |
||
| 8439 | exif: { |
||
| 8440 | 0x9000: 'ExifVersion', |
||
| 8441 | 0xA001: 'ColorSpace', |
||
| 8442 | 0xA002: 'PixelXDimension', |
||
| 8443 | 0xA003: 'PixelYDimension', |
||
| 8444 | 0x9003: 'DateTimeOriginal', |
||
| 8445 | 0x829A: 'ExposureTime', |
||
| 8446 | 0x829D: 'FNumber', |
||
| 8447 | 0x8827: 'ISOSpeedRatings', |
||
| 8448 | 0x9201: 'ShutterSpeedValue', |
||
| 8449 | 0x9202: 'ApertureValue' , |
||
| 8450 | 0x9207: 'MeteringMode', |
||
| 8451 | 0x9208: 'LightSource', |
||
| 8452 | 0x9209: 'Flash', |
||
| 8453 | 0x920A: 'FocalLength', |
||
| 8454 | 0xA402: 'ExposureMode', |
||
| 8455 | 0xA403: 'WhiteBalance', |
||
| 8456 | 0xA406: 'SceneCaptureType', |
||
| 8457 | 0xA404: 'DigitalZoomRatio', |
||
| 8458 | 0xA408: 'Contrast', |
||
| 8459 | 0xA409: 'Saturation', |
||
| 8460 | 0xA40A: 'Sharpness' |
||
| 8461 | }, |
||
| 8462 | gps: { |
||
| 8463 | 0x0000: 'GPSVersionID', |
||
| 8464 | 0x0001: 'GPSLatitudeRef', |
||
| 8465 | 0x0002: 'GPSLatitude', |
||
| 8466 | 0x0003: 'GPSLongitudeRef', |
||
| 8467 | 0x0004: 'GPSLongitude' |
||
| 8468 | }, |
||
| 8469 | |||
| 8470 | thumb: { |
||
| 8471 | 0x0201: 'JPEGInterchangeFormat', |
||
| 8472 | 0x0202: 'JPEGInterchangeFormatLength' |
||
| 8473 | } |
||
| 8474 | }; |
||
| 8475 | |||
| 8476 | tagDescs = { |
||
| 8477 | 'ColorSpace': { |
||
| 8478 | 1: 'sRGB', |
||
| 8479 | 0: 'Uncalibrated' |
||
| 8480 | }, |
||
| 8481 | |||
| 8482 | 'MeteringMode': { |
||
| 8483 | 0: 'Unknown', |
||
| 8484 | 1: 'Average', |
||
| 8485 | 2: 'CenterWeightedAverage', |
||
| 8486 | 3: 'Spot', |
||
| 8487 | 4: 'MultiSpot', |
||
| 8488 | 5: 'Pattern', |
||
| 8489 | 6: 'Partial', |
||
| 8490 | 255: 'Other' |
||
| 8491 | }, |
||
| 8492 | |||
| 8493 | 'LightSource': { |
||
| 8494 | 1: 'Daylight', |
||
| 8495 | 2: 'Fliorescent', |
||
| 8496 | 3: 'Tungsten', |
||
| 8497 | 4: 'Flash', |
||
| 8498 | 9: 'Fine weather', |
||
| 8499 | 10: 'Cloudy weather', |
||
| 8500 | 11: 'Shade', |
||
| 8501 | 12: 'Daylight fluorescent (D 5700 - 7100K)', |
||
| 8502 | 13: 'Day white fluorescent (N 4600 -5400K)', |
||
| 8503 | 14: 'Cool white fluorescent (W 3900 - 4500K)', |
||
| 8504 | 15: 'White fluorescent (WW 3200 - 3700K)', |
||
| 8505 | 17: 'Standard light A', |
||
| 8506 | 18: 'Standard light B', |
||
| 8507 | 19: 'Standard light C', |
||
| 8508 | 20: 'D55', |
||
| 8509 | 21: 'D65', |
||
| 8510 | 22: 'D75', |
||
| 8511 | 23: 'D50', |
||
| 8512 | 24: 'ISO studio tungsten', |
||
| 8513 | 255: 'Other' |
||
| 8514 | }, |
||
| 8515 | |||
| 8516 | 'Flash': { |
||
| 8517 | 0x0000: 'Flash did not fire', |
||
| 8518 | 0x0001: 'Flash fired', |
||
| 8519 | 0x0005: 'Strobe return light not detected', |
||
| 8520 | 0x0007: 'Strobe return light detected', |
||
| 8521 | 0x0009: 'Flash fired, compulsory flash mode', |
||
| 8522 | 0x000D: 'Flash fired, compulsory flash mode, return light not detected', |
||
| 8523 | 0x000F: 'Flash fired, compulsory flash mode, return light detected', |
||
| 8524 | 0x0010: 'Flash did not fire, compulsory flash mode', |
||
| 8525 | 0x0018: 'Flash did not fire, auto mode', |
||
| 8526 | 0x0019: 'Flash fired, auto mode', |
||
| 8527 | 0x001D: 'Flash fired, auto mode, return light not detected', |
||
| 8528 | 0x001F: 'Flash fired, auto mode, return light detected', |
||
| 8529 | 0x0020: 'No flash function', |
||
| 8530 | 0x0041: 'Flash fired, red-eye reduction mode', |
||
| 8531 | 0x0045: 'Flash fired, red-eye reduction mode, return light not detected', |
||
| 8532 | 0x0047: 'Flash fired, red-eye reduction mode, return light detected', |
||
| 8533 | 0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode', |
||
| 8534 | 0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', |
||
| 8535 | 0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', |
||
| 8536 | 0x0059: 'Flash fired, auto mode, red-eye reduction mode', |
||
| 8537 | 0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode', |
||
| 8538 | 0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode' |
||
| 8539 | }, |
||
| 8540 | |||
| 8541 | 'ExposureMode': { |
||
| 8542 | 0: 'Auto exposure', |
||
| 8543 | 1: 'Manual exposure', |
||
| 8544 | 2: 'Auto bracket' |
||
| 8545 | }, |
||
| 8546 | |||
| 8547 | 'WhiteBalance': { |
||
| 8548 | 0: 'Auto white balance', |
||
| 8549 | 1: 'Manual white balance' |
||
| 8550 | }, |
||
| 8551 | |||
| 8552 | 'SceneCaptureType': { |
||
| 8553 | 0: 'Standard', |
||
| 8554 | 1: 'Landscape', |
||
| 8555 | 2: 'Portrait', |
||
| 8556 | 3: 'Night scene' |
||
| 8557 | }, |
||
| 8558 | |||
| 8559 | 'Contrast': { |
||
| 8560 | 0: 'Normal', |
||
| 8561 | 1: 'Soft', |
||
| 8562 | 2: 'Hard' |
||
| 8563 | }, |
||
| 8564 | |||
| 8565 | 'Saturation': { |
||
| 8566 | 0: 'Normal', |
||
| 8567 | 1: 'Low saturation', |
||
| 8568 | 2: 'High saturation' |
||
| 8569 | }, |
||
| 8570 | |||
| 8571 | 'Sharpness': { |
||
| 8572 | 0: 'Normal', |
||
| 8573 | 1: 'Soft', |
||
| 8574 | 2: 'Hard' |
||
| 8575 | }, |
||
| 8576 | |||
| 8577 | // GPS related |
||
| 8578 | 'GPSLatitudeRef': { |
||
| 8579 | N: 'North latitude', |
||
| 8580 | S: 'South latitude' |
||
| 8581 | }, |
||
| 8582 | |||
| 8583 | 'GPSLongitudeRef': { |
||
| 8584 | E: 'East longitude', |
||
| 8585 | W: 'West longitude' |
||
| 8586 | } |
||
| 8587 | }; |
||
| 8588 | |||
| 8589 | offsets = { |
||
| 8590 | tiffHeader: 10 |
||
| 8591 | }; |
||
| 8592 | |||
| 8593 | idx = offsets.tiffHeader; |
||
| 8594 | |||
| 8595 | __super__ = { |
||
| 8596 | clear: this.clear |
||
| 8597 | }; |
||
| 8598 | |||
| 8599 | // Public functions |
||
| 8600 | Basic.extend(this, { |
||
| 8601 | |||
| 8602 | read: function() { |
||
| 8603 | try { |
||
| 8604 | return ExifParser.prototype.read.apply(this, arguments); |
||
| 8605 | } catch (ex) { |
||
| 8606 | throw new x.ImageError(x.ImageError.INVALID_META_ERR); |
||
| 8607 | } |
||
| 8608 | }, |
||
| 8609 | |||
| 8610 | |||
| 8611 | write: function() { |
||
| 8612 | try { |
||
| 8613 | return ExifParser.prototype.write.apply(this, arguments); |
||
| 8614 | } catch (ex) { |
||
| 8615 | throw new x.ImageError(x.ImageError.INVALID_META_ERR); |
||
| 8616 | } |
||
| 8617 | }, |
||
| 8618 | |||
| 8619 | |||
| 8620 | UNDEFINED: function() { |
||
| 8621 | return this.BYTE.apply(this, arguments); |
||
| 8622 | }, |
||
| 8623 | |||
| 8624 | |||
| 8625 | RATIONAL: function(idx) { |
||
| 8626 | return this.LONG(idx) / this.LONG(idx + 4) |
||
| 8627 | }, |
||
| 8628 | |||
| 8629 | |||
| 8630 | SRATIONAL: function(idx) { |
||
| 8631 | return this.SLONG(idx) / this.SLONG(idx + 4) |
||
| 8632 | }, |
||
| 8633 | |||
| 8634 | ASCII: function(idx) { |
||
| 8635 | return this.CHAR(idx); |
||
| 8636 | }, |
||
| 8637 | |||
| 8638 | TIFF: function() { |
||
| 8639 | return Tiff || null; |
||
| 8640 | }, |
||
| 8641 | |||
| 8642 | |||
| 8643 | EXIF: function() { |
||
| 8644 | var Exif = null; |
||
| 8645 | |||
| 8646 | if (offsets.exifIFD) { |
||
| 8647 | try { |
||
| 8648 | Exif = extractTags.call(this, offsets.exifIFD, tags.exif); |
||
| 8649 | } catch(ex) { |
||
| 8650 | return null; |
||
| 8651 | } |
||
| 8652 | |||
| 8653 | // Fix formatting of some tags |
||
| 8654 | if (Exif.ExifVersion && Basic.typeOf(Exif.ExifVersion) === 'array') { |
||
| 8655 | for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) { |
||
| 8656 | exifVersion += String.fromCharCode(Exif.ExifVersion[i]); |
||
| 8657 | } |
||
| 8658 | Exif.ExifVersion = exifVersion; |
||
| 8659 | } |
||
| 8660 | } |
||
| 8661 | |||
| 8662 | return Exif; |
||
| 8663 | }, |
||
| 8664 | |||
| 8665 | |||
| 8666 | GPS: function() { |
||
| 8667 | var GPS = null; |
||
| 8668 | |||
| 8669 | if (offsets.gpsIFD) { |
||
| 8670 | try { |
||
| 8671 | GPS = extractTags.call(this, offsets.gpsIFD, tags.gps); |
||
| 8672 | } catch (ex) { |
||
| 8673 | return null; |
||
| 8674 | } |
||
| 8675 | |||
| 8676 | // iOS devices (and probably some others) do not put in GPSVersionID tag (why?..) |
||
| 8677 | if (GPS.GPSVersionID && Basic.typeOf(GPS.GPSVersionID) === 'array') { |
||
| 8678 | GPS.GPSVersionID = GPS.GPSVersionID.join('.'); |
||
| 8679 | } |
||
| 8680 | } |
||
| 8681 | |||
| 8682 | return GPS; |
||
| 8683 | }, |
||
| 8684 | |||
| 8685 | |||
| 8686 | thumb: function() { |
||
| 8687 | if (offsets.IFD1) { |
||
| 8688 | try { |
||
| 8689 | var IFD1Tags = extractTags.call(this, offsets.IFD1, tags.thumb); |
||
| 8690 | |||
| 8691 | if ('JPEGInterchangeFormat' in IFD1Tags) { |
||
| 8692 | return this.SEGMENT(offsets.tiffHeader + IFD1Tags.JPEGInterchangeFormat, IFD1Tags.JPEGInterchangeFormatLength); |
||
| 8693 | } |
||
| 8694 | } catch (ex) {} |
||
| 8695 | } |
||
| 8696 | return null; |
||
| 8697 | }, |
||
| 8698 | |||
| 8699 | |||
| 8700 | setExif: function(tag, value) { |
||
| 8701 | // Right now only setting of width/height is possible |
||
| 8702 | if (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') { return false; } |
||
| 8703 | |||
| 8704 | return setTag.call(this, 'exif', tag, value); |
||
| 8705 | }, |
||
| 8706 | |||
| 8707 | |||
| 8708 | clear: function() { |
||
| 8709 | __super__.clear(); |
||
| 8710 | data = tags = tagDescs = Tiff = offsets = __super__ = null; |
||
| 8711 | } |
||
| 8712 | }); |
||
| 8713 | |||
| 8714 | |||
| 8715 | // Check if that's APP1 and that it has EXIF |
||
| 8716 | if (this.SHORT(0) !== 0xFFE1 || this.STRING(4, 5).toUpperCase() !== "EXIF\0") { |
||
| 8717 | throw new x.ImageError(x.ImageError.INVALID_META_ERR); |
||
| 8718 | } |
||
| 8719 | |||
| 8720 | // Set read order of multi-byte data |
||
| 8721 | this.littleEndian = (this.SHORT(idx) == 0x4949); |
||
| 8722 | |||
| 8723 | // Check if always present bytes are indeed present |
||
| 8724 | if (this.SHORT(idx+=2) !== 0x002A) { |
||
| 8725 | throw new x.ImageError(x.ImageError.INVALID_META_ERR); |
||
| 8726 | } |
||
| 8727 | |||
| 8728 | offsets.IFD0 = offsets.tiffHeader + this.LONG(idx += 2); |
||
| 8729 | Tiff = extractTags.call(this, offsets.IFD0, tags.tiff); |
||
| 8730 | |||
| 8731 | if ('ExifIFDPointer' in Tiff) { |
||
| 8732 | offsets.exifIFD = offsets.tiffHeader + Tiff.ExifIFDPointer; |
||
| 8733 | delete Tiff.ExifIFDPointer; |
||
| 8734 | } |
||
| 8735 | |||
| 8736 | if ('GPSInfoIFDPointer' in Tiff) { |
||
| 8737 | offsets.gpsIFD = offsets.tiffHeader + Tiff.GPSInfoIFDPointer; |
||
| 8738 | delete Tiff.GPSInfoIFDPointer; |
||
| 8739 | } |
||
| 8740 | |||
| 8741 | if (Basic.isEmptyObj(Tiff)) { |
||
| 8742 | Tiff = null; |
||
| 8743 | } |
||
| 8744 | |||
| 8745 | // check if we have a thumb as well |
||
| 8746 | var IFD1Offset = this.LONG(offsets.IFD0 + this.SHORT(offsets.IFD0) * 12 + 2); |
||
| 8747 | if (IFD1Offset) { |
||
| 8748 | offsets.IFD1 = offsets.tiffHeader + IFD1Offset; |
||
| 8749 | } |
||
| 8750 | |||
| 8751 | |||
| 8752 | function extractTags(IFD_offset, tags2extract) { |
||
| 8753 | var data = this; |
||
| 8754 | var length, i, tag, type, count, size, offset, value, values = [], hash = {}; |
||
| 8755 | |||
| 8756 | var types = { |
||
| 8757 | 1 : 'BYTE', |
||
| 8758 | 7 : 'UNDEFINED', |
||
| 8759 | 2 : 'ASCII', |
||
| 8760 | 3 : 'SHORT', |
||
| 8761 | 4 : 'LONG', |
||
| 8762 | 5 : 'RATIONAL', |
||
| 8763 | 9 : 'SLONG', |
||
| 8764 | 10: 'SRATIONAL' |
||
| 8765 | }; |
||
| 8766 | |||
| 8767 | var sizes = { |
||
| 8768 | 'BYTE' : 1, |
||
| 8769 | 'UNDEFINED' : 1, |
||
| 8770 | 'ASCII' : 1, |
||
| 8771 | 'SHORT' : 2, |
||
| 8772 | 'LONG' : 4, |
||
| 8773 | 'RATIONAL' : 8, |
||
| 8774 | 'SLONG' : 4, |
||
| 8775 | 'SRATIONAL' : 8 |
||
| 8776 | }; |
||
| 8777 | |||
| 8778 | length = data.SHORT(IFD_offset); |
||
| 8779 | |||
| 8780 | // The size of APP1 including all these elements shall not exceed the 64 Kbytes specified in the JPEG standard. |
||
| 8781 | |||
| 8782 | for (i = 0; i < length; i++) { |
||
| 8783 | values = []; |
||
| 8784 | |||
| 8785 | // Set binary reader pointer to beginning of the next tag |
||
| 8786 | offset = IFD_offset + 2 + i*12; |
||
| 8787 | |||
| 8788 | tag = tags2extract[data.SHORT(offset)]; |
||
| 8789 | |||
| 8790 | if (tag === undefined) { |
||
| 8791 | continue; // Not the tag we requested |
||
| 8792 | } |
||
| 8793 | |||
| 8794 | type = types[data.SHORT(offset+=2)]; |
||
| 8795 | count = data.LONG(offset+=2); |
||
| 8796 | size = sizes[type]; |
||
| 8797 | |||
| 8798 | if (!size) { |
||
| 8799 | throw new x.ImageError(x.ImageError.INVALID_META_ERR); |
||
| 8800 | } |
||
| 8801 | |||
| 8802 | offset += 4; |
||
| 8803 | |||
| 8804 | // tag can only fit 4 bytes of data, if data is larger we should look outside |
||
| 8805 | if (size * count > 4) { |
||
| 8806 | // instead of data tag contains an offset of the data |
||
| 8807 | offset = data.LONG(offset) + offsets.tiffHeader; |
||
| 8808 | } |
||
| 8809 | |||
| 8810 | // in case we left the boundaries of data throw an early exception |
||
| 8811 | if (offset + size * count >= this.length()) { |
||
| 8812 | throw new x.ImageError(x.ImageError.INVALID_META_ERR); |
||
| 8813 | } |
||
| 8814 | |||
| 8815 | // special care for the string |
||
| 8816 | if (type === 'ASCII') { |
||
| 8817 | hash[tag] = Basic.trim(data.STRING(offset, count).replace(/\0$/, '')); // strip trailing NULL |
||
| 8818 | continue; |
||
| 8819 | } else { |
||
| 8820 | values = data.asArray(type, offset, count); |
||
| 8821 | value = (count == 1 ? values[0] : values); |
||
| 8822 | |||
| 8823 | if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') { |
||
| 8824 | hash[tag] = tagDescs[tag][value]; |
||
| 8825 | } else { |
||
| 8826 | hash[tag] = value; |
||
| 8827 | } |
||
| 8828 | } |
||
| 8829 | } |
||
| 8830 | |||
| 8831 | return hash; |
||
| 8832 | } |
||
| 8833 | |||
| 8834 | // At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported |
||
| 8835 | function setTag(ifd, tag, value) { |
||
| 8836 | var offset, length, tagOffset, valueOffset = 0; |
||
| 8837 | |||
| 8838 | // If tag name passed translate into hex key |
||
| 8839 | if (typeof(tag) === 'string') { |
||
| 8840 | var tmpTags = tags[ifd.toLowerCase()]; |
||
| 8841 | for (var hex in tmpTags) { |
||
| 8842 | if (tmpTags[hex] === tag) { |
||
| 8843 | tag = hex; |
||
| 8844 | break; |
||
| 8845 | } |
||
| 8846 | } |
||
| 8847 | } |
||
| 8848 | offset = offsets[ifd.toLowerCase() + 'IFD']; |
||
| 8849 | length = this.SHORT(offset); |
||
| 8850 | |||
| 8851 | for (var i = 0; i < length; i++) { |
||
| 8852 | tagOffset = offset + 12 * i + 2; |
||
| 8853 | |||
| 8854 | if (this.SHORT(tagOffset) == tag) { |
||
| 8855 | valueOffset = tagOffset + 8; |
||
| 8856 | break; |
||
| 8857 | } |
||
| 8858 | } |
||
| 8859 | |||
| 8860 | if (!valueOffset) { |
||
| 8861 | return false; |
||
| 8862 | } |
||
| 8863 | |||
| 8864 | try { |
||
| 8865 | this.write(valueOffset, value, 4); |
||
| 8866 | } catch(ex) { |
||
| 8867 | return false; |
||
| 8868 | } |
||
| 8869 | |||
| 8870 | return true; |
||
| 8871 | } |
||
| 8872 | } |
||
| 8873 | |||
| 8874 | ExifParser.prototype = BinaryReader.prototype; |
||
| 8875 | |||
| 8876 | return ExifParser; |
||
| 8877 | }); |
||
| 8878 | |||
| 8879 | // Included from: src/javascript/runtime/html5/image/JPEG.js |
||
| 8880 | |||
| 8881 | /** |
||
| 8882 | * JPEG.js |
||
| 8883 | * |
||
| 8884 | * Copyright 2013, Moxiecode Systems AB |
||
| 8885 | * Released under GPL License. |
||
| 8886 | * |
||
| 8887 | * License: http://www.plupload.com/license |
||
| 8888 | * Contributing: http://www.plupload.com/contributing |
||
| 8889 | */ |
||
| 8890 | |||
| 8891 | /** |
||
| 8892 | @class moxie/runtime/html5/image/JPEG |
||
| 8893 | @private |
||
| 8894 | */ |
||
| 8895 | define("moxie/runtime/html5/image/JPEG", [ |
||
| 8896 | "moxie/core/utils/Basic", |
||
| 8897 | "moxie/core/Exceptions", |
||
| 8898 | "moxie/runtime/html5/image/JPEGHeaders", |
||
| 8899 | "moxie/runtime/html5/utils/BinaryReader", |
||
| 8900 | "moxie/runtime/html5/image/ExifParser" |
||
| 8901 | ], function(Basic, x, JPEGHeaders, BinaryReader, ExifParser) { |
||
| 8902 | |||
| 8903 | function JPEG(data) { |
||
| 8904 | var _br, _hm, _ep, _info; |
||
| 8905 | |||
| 8906 | _br = new BinaryReader(data); |
||
| 8907 | |||
| 8908 | // check if it is jpeg |
||
| 8909 | if (_br.SHORT(0) !== 0xFFD8) { |
||
| 8910 | throw new x.ImageError(x.ImageError.WRONG_FORMAT); |
||
| 8911 | } |
||
| 8912 | |||
| 8913 | // backup headers |
||
| 8914 | _hm = new JPEGHeaders(data); |
||
| 8915 | |||
| 8916 | // extract exif info |
||
| 8917 | try { |
||
| 8918 | _ep = new ExifParser(_hm.get('app1')[0]); |
||
| 8919 | } catch(ex) {} |
||
| 8920 | |||
| 8921 | // get dimensions |
||
| 8922 | _info = _getDimensions.call(this); |
||
| 8923 | |||
| 8924 | Basic.extend(this, { |
||
| 8925 | type: 'image/jpeg', |
||
| 8926 | |||
| 8927 | size: _br.length(), |
||
| 8928 | |||
| 8929 | width: _info && _info.width || 0, |
||
| 8930 | |||
| 8931 | height: _info && _info.height || 0, |
||
| 8932 | |||
| 8933 | setExif: function(tag, value) { |
||
| 8934 | if (!_ep) { |
||
| 8935 | return false; // or throw an exception |
||
| 8936 | } |
||
| 8937 | |||
| 8938 | if (Basic.typeOf(tag) === 'object') { |
||
| 8939 | Basic.each(tag, function(value, tag) { |
||
| 8940 | _ep.setExif(tag, value); |
||
| 8941 | }); |
||
| 8942 | } else { |
||
| 8943 | _ep.setExif(tag, value); |
||
| 8944 | } |
||
| 8945 | |||
| 8946 | // update internal headers |
||
| 8947 | _hm.set('app1', _ep.SEGMENT()); |
||
| 8948 | }, |
||
| 8949 | |||
| 8950 | writeHeaders: function() { |
||
| 8951 | if (!arguments.length) { |
||
| 8952 | // if no arguments passed, update headers internally |
||
| 8953 | return _hm.restore(data); |
||
| 8954 | } |
||
| 8955 | return _hm.restore(arguments[0]); |
||
| 8956 | }, |
||
| 8957 | |||
| 8958 | stripHeaders: function(data) { |
||
| 8959 | return _hm.strip(data); |
||
| 8960 | }, |
||
| 8961 | |||
| 8962 | purge: function() { |
||
| 8963 | _purge.call(this); |
||
| 8964 | } |
||
| 8965 | }); |
||
| 8966 | |||
| 8967 | if (_ep) { |
||
| 8968 | this.meta = { |
||
| 8969 | tiff: _ep.TIFF(), |
||
| 8970 | exif: _ep.EXIF(), |
||
| 8971 | gps: _ep.GPS(), |
||
| 8972 | thumb: _getThumb() |
||
| 8973 | }; |
||
| 8974 | } |
||
| 8975 | |||
| 8976 | |||
| 8977 | function _getDimensions(br) { |
||
| 8978 | var idx = 0 |
||
| 8979 | , marker |
||
| 8980 | , length |
||
| 8981 | ; |
||
| 8982 | |||
| 8983 | if (!br) { |
||
| 8984 | br = _br; |
||
| 8985 | } |
||
| 8986 | |||
| 8987 | // examine all through the end, since some images might have very large APP segments |
||
| 8988 | while (idx <= br.length()) { |
||
| 8989 | marker = br.SHORT(idx += 2); |
||
| 8990 | |||
| 8991 | if (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn |
||
| 8992 | idx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte) |
||
| 8993 | return { |
||
| 8994 | height: br.SHORT(idx), |
||
| 8995 | width: br.SHORT(idx += 2) |
||
| 8996 | }; |
||
| 8997 | } |
||
| 8998 | length = br.SHORT(idx += 2); |
||
| 8999 | idx += length - 2; |
||
| 9000 | } |
||
| 9001 | return null; |
||
| 9002 | } |
||
| 9003 | |||
| 9004 | |||
| 9005 | function _getThumb() { |
||
| 9006 | var data = _ep.thumb() |
||
| 9007 | , br |
||
| 9008 | , info |
||
| 9009 | ; |
||
| 9010 | |||
| 9011 | if (data) { |
||
| 9012 | br = new BinaryReader(data); |
||
| 9013 | info = _getDimensions(br); |
||
| 9014 | br.clear(); |
||
| 9015 | |||
| 9016 | if (info) { |
||
| 9017 | info.data = data; |
||
| 9018 | return info; |
||
| 9019 | } |
||
| 9020 | } |
||
| 9021 | return null; |
||
| 9022 | } |
||
| 9023 | |||
| 9024 | |||
| 9025 | function _purge() { |
||
| 9026 | if (!_ep || !_hm || !_br) { |
||
| 9027 | return; // ignore any repeating purge requests |
||
| 9028 | } |
||
| 9029 | _ep.clear(); |
||
| 9030 | _hm.purge(); |
||
| 9031 | _br.clear(); |
||
| 9032 | _info = _hm = _ep = _br = null; |
||
| 9033 | } |
||
| 9034 | } |
||
| 9035 | |||
| 9036 | return JPEG; |
||
| 9037 | }); |
||
| 9038 | |||
| 9039 | // Included from: src/javascript/runtime/html5/image/PNG.js |
||
| 9040 | |||
| 9041 | /** |
||
| 9042 | * PNG.js |
||
| 9043 | * |
||
| 9044 | * Copyright 2013, Moxiecode Systems AB |
||
| 9045 | * Released under GPL License. |
||
| 9046 | * |
||
| 9047 | * License: http://www.plupload.com/license |
||
| 9048 | * Contributing: http://www.plupload.com/contributing |
||
| 9049 | */ |
||
| 9050 | |||
| 9051 | /** |
||
| 9052 | @class moxie/runtime/html5/image/PNG |
||
| 9053 | @private |
||
| 9054 | */ |
||
| 9055 | define("moxie/runtime/html5/image/PNG", [ |
||
| 9056 | "moxie/core/Exceptions", |
||
| 9057 | "moxie/core/utils/Basic", |
||
| 9058 | "moxie/runtime/html5/utils/BinaryReader" |
||
| 9059 | ], function(x, Basic, BinaryReader) { |
||
| 9060 | |||
| 9061 | function PNG(data) { |
||
| 9062 | var _br, _hm, _ep, _info; |
||
| 9063 | |||
| 9064 | _br = new BinaryReader(data); |
||
| 9065 | |||
| 9066 | // check if it's png |
||
| 9067 | (function() { |
||
| 9068 | var idx = 0, i = 0 |
||
| 9069 | , signature = [0x8950, 0x4E47, 0x0D0A, 0x1A0A] |
||
| 9070 | ; |
||
| 9071 | |||
| 9072 | for (i = 0; i < signature.length; i++, idx += 2) { |
||
| 9073 | if (signature[i] != _br.SHORT(idx)) { |
||
| 9074 | throw new x.ImageError(x.ImageError.WRONG_FORMAT); |
||
| 9075 | } |
||
| 9076 | } |
||
| 9077 | }()); |
||
| 9078 | |||
| 9079 | function _getDimensions() { |
||
| 9080 | var chunk, idx; |
||
| 9081 | |||
| 9082 | chunk = _getChunkAt.call(this, 8); |
||
| 9083 | |||
| 9084 | if (chunk.type == 'IHDR') { |
||
| 9085 | idx = chunk.start; |
||
| 9086 | return { |
||
| 9087 | width: _br.LONG(idx), |
||
| 9088 | height: _br.LONG(idx += 4) |
||
| 9089 | }; |
||
| 9090 | } |
||
| 9091 | return null; |
||
| 9092 | } |
||
| 9093 | |||
| 9094 | function _purge() { |
||
| 9095 | if (!_br) { |
||
| 9096 | return; // ignore any repeating purge requests |
||
| 9097 | } |
||
| 9098 | _br.clear(); |
||
| 9099 | data = _info = _hm = _ep = _br = null; |
||
| 9100 | } |
||
| 9101 | |||
| 9102 | _info = _getDimensions.call(this); |
||
| 9103 | |||
| 9104 | Basic.extend(this, { |
||
| 9105 | type: 'image/png', |
||
| 9106 | |||
| 9107 | size: _br.length(), |
||
| 9108 | |||
| 9109 | width: _info.width, |
||
| 9110 | |||
| 9111 | height: _info.height, |
||
| 9112 | |||
| 9113 | purge: function() { |
||
| 9114 | _purge.call(this); |
||
| 9115 | } |
||
| 9116 | }); |
||
| 9117 | |||
| 9118 | // for PNG we can safely trigger purge automatically, as we do not keep any data for later |
||
| 9119 | _purge.call(this); |
||
| 9120 | |||
| 9121 | function _getChunkAt(idx) { |
||
| 9122 | var length, type, start, CRC; |
||
| 9123 | |||
| 9124 | length = _br.LONG(idx); |
||
| 9125 | type = _br.STRING(idx += 4, 4); |
||
| 9126 | start = idx += 4; |
||
| 9127 | CRC = _br.LONG(idx + length); |
||
| 9128 | |||
| 9129 | return { |
||
| 9130 | length: length, |
||
| 9131 | type: type, |
||
| 9132 | start: start, |
||
| 9133 | CRC: CRC |
||
| 9134 | }; |
||
| 9135 | } |
||
| 9136 | } |
||
| 9137 | |||
| 9138 | return PNG; |
||
| 9139 | }); |
||
| 9140 | |||
| 9141 | // Included from: src/javascript/runtime/html5/image/ImageInfo.js |
||
| 9142 | |||
| 9143 | /** |
||
| 9144 | * ImageInfo.js |
||
| 9145 | * |
||
| 9146 | * Copyright 2013, Moxiecode Systems AB |
||
| 9147 | * Released under GPL License. |
||
| 9148 | * |
||
| 9149 | * License: http://www.plupload.com/license |
||
| 9150 | * Contributing: http://www.plupload.com/contributing |
||
| 9151 | */ |
||
| 9152 | |||
| 9153 | /** |
||
| 9154 | Optional image investigation tool for HTML5 runtime. Provides the following features: |
||
| 9155 | - ability to distinguish image type (JPEG or PNG) by signature |
||
| 9156 | - ability to extract image width/height directly from it's internals, without preloading in memory (fast) |
||
| 9157 | - ability to extract APP headers from JPEGs (Exif, GPS, etc) |
||
| 9158 | - ability to replace width/height tags in extracted JPEG headers |
||
| 9159 | - ability to restore APP headers, that were for example stripped during image manipulation |
||
| 9160 | |||
| 9161 | @class moxie/runtime/html5/image/ImageInfo |
||
| 9162 | @private |
||
| 9163 | @param {String} data Image source as binary string |
||
| 9164 | */ |
||
| 9165 | define("moxie/runtime/html5/image/ImageInfo", [ |
||
| 9166 | "moxie/core/utils/Basic", |
||
| 9167 | "moxie/core/Exceptions", |
||
| 9168 | "moxie/runtime/html5/image/JPEG", |
||
| 9169 | "moxie/runtime/html5/image/PNG" |
||
| 9170 | ], function(Basic, x, JPEG, PNG) { |
||
| 9171 | |||
| 9172 | return function(data) { |
||
| 9173 | var _cs = [JPEG, PNG], _img; |
||
| 9174 | |||
| 9175 | // figure out the format, throw: ImageError.WRONG_FORMAT if not supported |
||
| 9176 | _img = (function() { |
||
| 9177 | for (var i = 0; i < _cs.length; i++) { |
||
| 9178 | try { |
||
| 9179 | return new _cs[i](data); |
||
| 9180 | } catch (ex) { |
||
| 9181 | // console.info(ex); |
||
| 9182 | } |
||
| 9183 | } |
||
| 9184 | throw new x.ImageError(x.ImageError.WRONG_FORMAT); |
||
| 9185 | }()); |
||
| 9186 | |||
| 9187 | Basic.extend(this, { |
||
| 9188 | /** |
||
| 9189 | Image Mime Type extracted from it's depths |
||
| 9190 | |||
| 9191 | @property type |
||
| 9192 | @type {String} |
||
| 9193 | @default '' |
||
| 9194 | */ |
||
| 9195 | type: '', |
||
| 9196 | |||
| 9197 | /** |
||
| 9198 | Image size in bytes |
||
| 9199 | |||
| 9200 | @property size |
||
| 9201 | @type {Number} |
||
| 9202 | @default 0 |
||
| 9203 | */ |
||
| 9204 | size: 0, |
||
| 9205 | |||
| 9206 | /** |
||
| 9207 | Image width extracted from image source |
||
| 9208 | |||
| 9209 | @property width |
||
| 9210 | @type {Number} |
||
| 9211 | @default 0 |
||
| 9212 | */ |
||
| 9213 | width: 0, |
||
| 9214 | |||
| 9215 | /** |
||
| 9216 | Image height extracted from image source |
||
| 9217 | |||
| 9218 | @property height |
||
| 9219 | @type {Number} |
||
| 9220 | @default 0 |
||
| 9221 | */ |
||
| 9222 | height: 0, |
||
| 9223 | |||
| 9224 | /** |
||
| 9225 | Sets Exif tag. Currently applicable only for width and height tags. Obviously works only with JPEGs. |
||
| 9226 | |||
| 9227 | @method setExif |
||
| 9228 | @param {String} tag Tag to set |
||
| 9229 | @param {Mixed} value Value to assign to the tag |
||
| 9230 | */ |
||
| 9231 | setExif: function() {}, |
||
| 9232 | |||
| 9233 | /** |
||
| 9234 | Restores headers to the source. |
||
| 9235 | |||
| 9236 | @method writeHeaders |
||
| 9237 | @param {String} data Image source as binary string |
||
| 9238 | @return {String} Updated binary string |
||
| 9239 | */ |
||
| 9240 | writeHeaders: function(data) { |
||
| 9241 | return data; |
||
| 9242 | }, |
||
| 9243 | |||
| 9244 | /** |
||
| 9245 | Strip all headers from the source. |
||
| 9246 | |||
| 9247 | @method stripHeaders |
||
| 9248 | @param {String} data Image source as binary string |
||
| 9249 | @return {String} Updated binary string |
||
| 9250 | */ |
||
| 9251 | stripHeaders: function(data) { |
||
| 9252 | return data; |
||
| 9253 | }, |
||
| 9254 | |||
| 9255 | /** |
||
| 9256 | Dispose resources. |
||
| 9257 | |||
| 9258 | @method purge |
||
| 9259 | */ |
||
| 9260 | purge: function() { |
||
| 9261 | data = null; |
||
| 9262 | } |
||
| 9263 | }); |
||
| 9264 | |||
| 9265 | Basic.extend(this, _img); |
||
| 9266 | |||
| 9267 | this.purge = function() { |
||
| 9268 | _img.purge(); |
||
| 9269 | _img = null; |
||
| 9270 | }; |
||
| 9271 | }; |
||
| 9272 | }); |
||
| 9273 | |||
| 9274 | // Included from: src/javascript/runtime/html5/image/ResizerCanvas.js |
||
| 9275 | |||
| 9276 | /** |
||
| 9277 | * ResizerCanvas.js |
||
| 9278 | * |
||
| 9279 | * Copyright 2013, Moxiecode Systems AB |
||
| 9280 | * Released under GPL License. |
||
| 9281 | * |
||
| 9282 | * License: http://www.plupload.com/license |
||
| 9283 | * Contributing: http://www.plupload.com/contributing |
||
| 9284 | */ |
||
| 9285 | |||
| 9286 | /** |
||
| 9287 | * Resizes image/canvas using canvas |
||
| 9288 | */ |
||
| 9289 | define("moxie/runtime/html5/image/ResizerCanvas", [], function() { |
||
| 9290 | |||
| 9291 | function scale(image, ratio, resample) { |
||
| 9292 | var sD = image.width > image.height ? 'width' : 'height'; // take the largest side |
||
| 9293 | var dD = Math.round(image[sD] * ratio); |
||
| 9294 | var scaleCapped = false; |
||
| 9295 | |||
| 9296 | if (resample !== 'nearest' && (ratio < 0.5 || ratio > 2)) { |
||
| 9297 | ratio = ratio < 0.5 ? 0.5 : 2; |
||
| 9298 | scaleCapped = true; |
||
| 9299 | } |
||
| 9300 | |||
| 9301 | var tCanvas = _scale(image, ratio); |
||
| 9302 | |||
| 9303 | if (scaleCapped) { |
||
| 9304 | return scale(tCanvas, dD / tCanvas[sD], resample); |
||
| 9305 | } else { |
||
| 9306 | return tCanvas; |
||
| 9307 | } |
||
| 9308 | } |
||
| 9309 | |||
| 9310 | |||
| 9311 | function _scale(image, ratio) { |
||
| 9312 | var sW = image.width; |
||
| 9313 | var sH = image.height; |
||
| 9314 | var dW = Math.round(sW * ratio); |
||
| 9315 | var dH = Math.round(sH * ratio); |
||
| 9316 | |||
| 9317 | var canvas = document.createElement('canvas'); |
||
| 9318 | canvas.width = dW; |
||
| 9319 | canvas.height = dH; |
||
| 9320 | canvas.getContext("2d").drawImage(image, 0, 0, sW, sH, 0, 0, dW, dH); |
||
| 9321 | |||
| 9322 | image = null; // just in case |
||
| 9323 | return canvas; |
||
| 9324 | } |
||
| 9325 | |||
| 9326 | return { |
||
| 9327 | scale: scale |
||
| 9328 | }; |
||
| 9329 | |||
| 9330 | }); |
||
| 9331 | |||
| 9332 | // Included from: src/javascript/runtime/html5/image/Image.js |
||
| 9333 | |||
| 9334 | /** |
||
| 9335 | * Image.js |
||
| 9336 | * |
||
| 9337 | * Copyright 2013, Moxiecode Systems AB |
||
| 9338 | * Released under GPL License. |
||
| 9339 | * |
||
| 9340 | * License: http://www.plupload.com/license |
||
| 9341 | * Contributing: http://www.plupload.com/contributing |
||
| 9342 | */ |
||
| 9343 | |||
| 9344 | /** |
||
| 9345 | @class moxie/runtime/html5/image/Image |
||
| 9346 | @private |
||
| 9347 | */ |
||
| 9348 | define("moxie/runtime/html5/image/Image", [ |
||
| 9349 | "moxie/runtime/html5/Runtime", |
||
| 9350 | "moxie/core/utils/Basic", |
||
| 9351 | "moxie/core/Exceptions", |
||
| 9352 | "moxie/core/utils/Encode", |
||
| 9353 | "moxie/file/Blob", |
||
| 9354 | "moxie/file/File", |
||
| 9355 | "moxie/runtime/html5/image/ImageInfo", |
||
| 9356 | "moxie/runtime/html5/image/ResizerCanvas", |
||
| 9357 | "moxie/core/utils/Mime", |
||
| 9358 | "moxie/core/utils/Env" |
||
| 9359 | ], function(extensions, Basic, x, Encode, Blob, File, ImageInfo, ResizerCanvas, Mime, Env) { |
||
| 9360 | |||
| 9361 | function HTML5Image() { |
||
| 9362 | var me = this |
||
| 9363 | , _img, _imgInfo, _canvas, _binStr, _blob |
||
| 9364 | , _modified = false // is set true whenever image is modified |
||
| 9365 | , _preserveHeaders = true |
||
| 9366 | ; |
||
| 9367 | |||
| 9368 | Basic.extend(this, { |
||
| 9369 | loadFromBlob: function(blob) { |
||
| 9370 | var I = this.getRuntime() |
||
| 9371 | , asBinary = arguments.length > 1 ? arguments[1] : true |
||
| 9372 | ; |
||
| 9373 | |||
| 9374 | if (!I.can('access_binary')) { |
||
| 9375 | throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR); |
||
| 9376 | } |
||
| 9377 | |||
| 9378 | _blob = blob; |
||
| 9379 | |||
| 9380 | if (blob.isDetached()) { |
||
| 9381 | _binStr = blob.getSource(); |
||
| 9382 | _preload.call(this, _binStr); |
||
| 9383 | return; |
||
| 9384 | } else { |
||
| 9385 | _readAsDataUrl.call(this, blob.getSource(), function(dataUrl) { |
||
| 9386 | if (asBinary) { |
||
| 9387 | _binStr = _toBinary(dataUrl); |
||
| 9388 | } |
||
| 9389 | _preload.call(this, dataUrl); |
||
| 9390 | }); |
||
| 9391 | } |
||
| 9392 | }, |
||
| 9393 | |||
| 9394 | loadFromImage: function(img, exact) { |
||
| 9395 | this.meta = img.meta; |
||
| 9396 | |||
| 9397 | _blob = new File(null, { |
||
| 9398 | name: img.name, |
||
| 9399 | size: img.size, |
||
| 9400 | type: img.type |
||
| 9401 | }); |
||
| 9402 | |||
| 9403 | _preload.call(this, exact ? (_binStr = img.getAsBinaryString()) : img.getAsDataURL()); |
||
| 9404 | }, |
||
| 9405 | |||
| 9406 | getInfo: function() { |
||
| 9407 | var I = this.getRuntime(), info; |
||
| 9408 | |||
| 9409 | if (!_imgInfo && _binStr && I.can('access_image_binary')) { |
||
| 9410 | _imgInfo = new ImageInfo(_binStr); |
||
| 9411 | } |
||
| 9412 | |||
| 9413 | // this stuff below is definitely having fun with itself |
||
| 9414 | info = { |
||
| 9415 | width: _getImg().width || 0, |
||
| 9416 | height: _getImg().height || 0, |
||
| 9417 | type: _blob.type || Mime.getFileMime(_blob.name), |
||
| 9418 | size: _binStr && _binStr.length || _blob.size || 0, |
||
| 9419 | name: _blob.name || '', |
||
| 9420 | meta: null |
||
| 9421 | }; |
||
| 9422 | |||
| 9423 | if (_preserveHeaders) { |
||
| 9424 | info.meta = _imgInfo && _imgInfo.meta || this.meta || {}; |
||
| 9425 | |||
| 9426 | // if data was taken from ImageInfo it will be a binary string, so we convert it to blob |
||
| 9427 | if (info.meta && info.meta.thumb && !(info.meta.thumb.data instanceof Blob)) { |
||
| 9428 | info.meta.thumb.data = new Blob(null, { |
||
| 9429 | type: 'image/jpeg', |
||
| 9430 | data: info.meta.thumb.data |
||
| 9431 | }); |
||
| 9432 | } |
||
| 9433 | } |
||
| 9434 | |||
| 9435 | return info; |
||
| 9436 | }, |
||
| 9437 | |||
| 9438 | |||
| 9439 | resize: function(rect, ratio, options) { |
||
| 9440 | var canvas = document.createElement('canvas'); |
||
| 9441 | canvas.width = rect.width; |
||
| 9442 | canvas.height = rect.height; |
||
| 9443 | |||
| 9444 | canvas.getContext("2d").drawImage(_getImg(), rect.x, rect.y, rect.width, rect.height, 0, 0, canvas.width, canvas.height); |
||
| 9445 | |||
| 9446 | _canvas = ResizerCanvas.scale(canvas, ratio); |
||
| 9447 | |||
| 9448 | _preserveHeaders = options.preserveHeaders; |
||
| 9449 | |||
| 9450 | // rotate if required, according to orientation tag |
||
| 9451 | if (!_preserveHeaders) { |
||
| 9452 | var orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1; |
||
| 9453 | _canvas = _rotateToOrientaion(_canvas, orientation); |
||
| 9454 | } |
||
| 9455 | |||
| 9456 | this.width = _canvas.width; |
||
| 9457 | this.height = _canvas.height; |
||
| 9458 | |||
| 9459 | _modified = true; |
||
| 9460 | |||
| 9461 | this.trigger('Resize'); |
||
| 9462 | }, |
||
| 9463 | |||
| 9464 | getAsCanvas: function() { |
||
| 9465 | if (!_canvas) { |
||
| 9466 | _canvas = _getCanvas(); |
||
| 9467 | } |
||
| 9468 | _canvas.id = this.uid + '_canvas'; |
||
| 9469 | return _canvas; |
||
| 9470 | }, |
||
| 9471 | |||
| 9472 | getAsBlob: function(type, quality) { |
||
| 9473 | if (type !== this.type) { |
||
| 9474 | _modified = true; // reconsider the state |
||
| 9475 | return new File(null, { |
||
| 9476 | name: _blob.name || '', |
||
| 9477 | type: type, |
||
| 9478 | data: me.getAsDataURL(type, quality) |
||
| 9479 | }); |
||
| 9480 | } |
||
| 9481 | return new File(null, { |
||
| 9482 | name: _blob.name || '', |
||
| 9483 | type: type, |
||
| 9484 | data: me.getAsBinaryString(type, quality) |
||
| 9485 | }); |
||
| 9486 | }, |
||
| 9487 | |||
| 9488 | getAsDataURL: function(type) { |
||
| 9489 | var quality = arguments[1] || 90; |
||
| 9490 | |||
| 9491 | // if image has not been modified, return the source right away |
||
| 9492 | if (!_modified) { |
||
| 9493 | return _img.src; |
||
| 9494 | } |
||
| 9495 | |||
| 9496 | // make sure we have a canvas to work with |
||
| 9497 | _getCanvas(); |
||
| 9498 | |||
| 9499 | if ('image/jpeg' !== type) { |
||
| 9500 | return _canvas.toDataURL('image/png'); |
||
| 9501 | } else { |
||
| 9502 | try { |
||
| 9503 | // older Geckos used to result in an exception on quality argument |
||
| 9504 | return _canvas.toDataURL('image/jpeg', quality/100); |
||
| 9505 | } catch (ex) { |
||
| 9506 | return _canvas.toDataURL('image/jpeg'); |
||
| 9507 | } |
||
| 9508 | } |
||
| 9509 | }, |
||
| 9510 | |||
| 9511 | getAsBinaryString: function(type, quality) { |
||
| 9512 | // if image has not been modified, return the source right away |
||
| 9513 | if (!_modified) { |
||
| 9514 | // if image was not loaded from binary string |
||
| 9515 | if (!_binStr) { |
||
| 9516 | _binStr = _toBinary(me.getAsDataURL(type, quality)); |
||
| 9517 | } |
||
| 9518 | return _binStr; |
||
| 9519 | } |
||
| 9520 | |||
| 9521 | if ('image/jpeg' !== type) { |
||
| 9522 | _binStr = _toBinary(me.getAsDataURL(type, quality)); |
||
| 9523 | } else { |
||
| 9524 | var dataUrl; |
||
| 9525 | |||
| 9526 | // if jpeg |
||
| 9527 | if (!quality) { |
||
| 9528 | quality = 90; |
||
| 9529 | } |
||
| 9530 | |||
| 9531 | // make sure we have a canvas to work with |
||
| 9532 | _getCanvas(); |
||
| 9533 | |||
| 9534 | try { |
||
| 9535 | // older Geckos used to result in an exception on quality argument |
||
| 9536 | dataUrl = _canvas.toDataURL('image/jpeg', quality/100); |
||
| 9537 | } catch (ex) { |
||
| 9538 | dataUrl = _canvas.toDataURL('image/jpeg'); |
||
| 9539 | } |
||
| 9540 | |||
| 9541 | _binStr = _toBinary(dataUrl); |
||
| 9542 | |||
| 9543 | if (_imgInfo) { |
||
| 9544 | _binStr = _imgInfo.stripHeaders(_binStr); |
||
| 9545 | |||
| 9546 | if (_preserveHeaders) { |
||
| 9547 | // update dimensions info in exif |
||
| 9548 | if (_imgInfo.meta && _imgInfo.meta.exif) { |
||
| 9549 | _imgInfo.setExif({ |
||
| 9550 | PixelXDimension: this.width, |
||
| 9551 | PixelYDimension: this.height |
||
| 9552 | }); |
||
| 9553 | } |
||
| 9554 | |||
| 9555 | // re-inject the headers |
||
| 9556 | _binStr = _imgInfo.writeHeaders(_binStr); |
||
| 9557 | } |
||
| 9558 | |||
| 9559 | // will be re-created from fresh on next getInfo call |
||
| 9560 | _imgInfo.purge(); |
||
| 9561 | _imgInfo = null; |
||
| 9562 | } |
||
| 9563 | } |
||
| 9564 | |||
| 9565 | _modified = false; |
||
| 9566 | |||
| 9567 | return _binStr; |
||
| 9568 | }, |
||
| 9569 | |||
| 9570 | destroy: function() { |
||
| 9571 | me = null; |
||
| 9572 | _purge.call(this); |
||
| 9573 | this.getRuntime().getShim().removeInstance(this.uid); |
||
| 9574 | } |
||
| 9575 | }); |
||
| 9576 | |||
| 9577 | |||
| 9578 | function _getImg() { |
||
| 9579 | if (!_canvas && !_img) { |
||
| 9580 | throw new x.ImageError(x.DOMException.INVALID_STATE_ERR); |
||
| 9581 | } |
||
| 9582 | return _canvas || _img; |
||
| 9583 | } |
||
| 9584 | |||
| 9585 | |||
| 9586 | function _getCanvas() { |
||
| 9587 | var canvas = _getImg(); |
||
| 9588 | if (canvas.nodeName.toLowerCase() == 'canvas') { |
||
| 9589 | return canvas; |
||
| 9590 | } |
||
| 9591 | _canvas = document.createElement('canvas'); |
||
| 9592 | _canvas.width = canvas.width; |
||
| 9593 | _canvas.height = canvas.height; |
||
| 9594 | _canvas.getContext("2d").drawImage(canvas, 0, 0); |
||
| 9595 | return _canvas; |
||
| 9596 | } |
||
| 9597 | |||
| 9598 | |||
| 9599 | function _toBinary(str) { |
||
| 9600 | return Encode.atob(str.substring(str.indexOf('base64,') + 7)); |
||
| 9601 | } |
||
| 9602 | |||
| 9603 | |||
| 9604 | function _toDataUrl(str, type) { |
||
| 9605 | return 'data:' + (type || '') + ';base64,' + Encode.btoa(str); |
||
| 9606 | } |
||
| 9607 | |||
| 9608 | |||
| 9609 | function _preload(str) { |
||
| 9610 | var comp = this; |
||
| 9611 | |||
| 9612 | _img = new Image(); |
||
| 9613 | _img.onerror = function() { |
||
| 9614 | _purge.call(this); |
||
| 9615 | comp.trigger('error', x.ImageError.WRONG_FORMAT); |
||
| 9616 | }; |
||
| 9617 | _img.onload = function() { |
||
| 9618 | comp.trigger('load'); |
||
| 9619 | }; |
||
| 9620 | |||
| 9621 | _img.src = str.substr(0, 5) == 'data:' ? str : _toDataUrl(str, _blob.type); |
||
| 9622 | } |
||
| 9623 | |||
| 9624 | |||
| 9625 | function _readAsDataUrl(file, callback) { |
||
| 9626 | var comp = this, fr; |
||
| 9627 | |||
| 9628 | // use FileReader if it's available |
||
| 9629 | if (window.FileReader) { |
||
| 9630 | fr = new FileReader(); |
||
| 9631 | fr.onload = function() { |
||
| 9632 | callback.call(comp, this.result); |
||
| 9633 | }; |
||
| 9634 | fr.onerror = function() { |
||
| 9635 | comp.trigger('error', x.ImageError.WRONG_FORMAT); |
||
| 9636 | }; |
||
| 9637 | fr.readAsDataURL(file); |
||
| 9638 | } else { |
||
| 9639 | return callback.call(this, file.getAsDataURL()); |
||
| 9640 | } |
||
| 9641 | } |
||
| 9642 | |||
| 9643 | /** |
||
| 9644 | * Transform canvas coordination according to specified frame size and orientation |
||
| 9645 | * Orientation value is from EXIF tag |
||
| 9646 | * @author Shinichi Tomita <[email protected]> |
||
| 9647 | */ |
||
| 9648 | function _rotateToOrientaion(img, orientation) { |
||
| 9649 | var RADIANS = Math.PI/180; |
||
| 9650 | var canvas = document.createElement('canvas'); |
||
| 9651 | var ctx = canvas.getContext('2d'); |
||
| 9652 | var width = img.width; |
||
| 9653 | var height = img.height; |
||
| 9654 | |||
| 9655 | if (Basic.inArray(orientation, [5,6,7,8]) > -1) { |
||
| 9656 | canvas.width = height; |
||
| 9657 | canvas.height = width; |
||
| 9658 | } else { |
||
| 9659 | canvas.width = width; |
||
| 9660 | canvas.height = height; |
||
| 9661 | } |
||
| 9662 | |||
| 9663 | /** |
||
| 9664 | 1 = The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. |
||
| 9665 | 2 = The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. |
||
| 9666 | 3 = The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side. |
||
| 9667 | 4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. |
||
| 9668 | 5 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. |
||
| 9669 | 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. |
||
| 9670 | 7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. |
||
| 9671 | 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. |
||
| 9672 | */ |
||
| 9673 | switch (orientation) { |
||
| 9674 | case 2: |
||
| 9675 | // horizontal flip |
||
| 9676 | ctx.translate(width, 0); |
||
| 9677 | ctx.scale(-1, 1); |
||
| 9678 | break; |
||
| 9679 | case 3: |
||
| 9680 | // 180 rotate left |
||
| 9681 | ctx.translate(width, height); |
||
| 9682 | ctx.rotate(180 * RADIANS); |
||
| 9683 | break; |
||
| 9684 | case 4: |
||
| 9685 | // vertical flip |
||
| 9686 | ctx.translate(0, height); |
||
| 9687 | ctx.scale(1, -1); |
||
| 9688 | break; |
||
| 9689 | case 5: |
||
| 9690 | // vertical flip + 90 rotate right |
||
| 9691 | ctx.rotate(90 * RADIANS); |
||
| 9692 | ctx.scale(1, -1); |
||
| 9693 | break; |
||
| 9694 | case 6: |
||
| 9695 | // 90 rotate right |
||
| 9696 | ctx.rotate(90 * RADIANS); |
||
| 9697 | ctx.translate(0, -height); |
||
| 9698 | break; |
||
| 9699 | case 7: |
||
| 9700 | // horizontal flip + 90 rotate right |
||
| 9701 | ctx.rotate(90 * RADIANS); |
||
| 9702 | ctx.translate(width, -height); |
||
| 9703 | ctx.scale(-1, 1); |
||
| 9704 | break; |
||
| 9705 | case 8: |
||
| 9706 | // 90 rotate left |
||
| 9707 | ctx.rotate(-90 * RADIANS); |
||
| 9708 | ctx.translate(-width, 0); |
||
| 9709 | break; |
||
| 9710 | } |
||
| 9711 | |||
| 9712 | ctx.drawImage(img, 0, 0, width, height); |
||
| 9713 | return canvas; |
||
| 9714 | } |
||
| 9715 | |||
| 9716 | |||
| 9717 | function _purge() { |
||
| 9718 | if (_imgInfo) { |
||
| 9719 | _imgInfo.purge(); |
||
| 9720 | _imgInfo = null; |
||
| 9721 | } |
||
| 9722 | |||
| 9723 | _binStr = _img = _canvas = _blob = null; |
||
| 9724 | _modified = false; |
||
| 9725 | } |
||
| 9726 | } |
||
| 9727 | |||
| 9728 | return (extensions.Image = HTML5Image); |
||
| 9729 | }); |
||
| 9730 | |||
| 9731 | // Included from: src/javascript/runtime/flash/Runtime.js |
||
| 9732 | |||
| 9733 | /** |
||
| 9734 | * Runtime.js |
||
| 9735 | * |
||
| 9736 | * Copyright 2013, Moxiecode Systems AB |
||
| 9737 | * Released under GPL License. |
||
| 9738 | * |
||
| 9739 | * License: http://www.plupload.com/license |
||
| 9740 | * Contributing: http://www.plupload.com/contributing |
||
| 9741 | */ |
||
| 9742 | |||
| 9743 | /*global ActiveXObject:true */ |
||
| 9744 | |||
| 9745 | /** |
||
| 9746 | Defines constructor for Flash runtime. |
||
| 9747 | |||
| 9748 | @class moxie/runtime/flash/Runtime |
||
| 9749 | @private |
||
| 9750 | */ |
||
| 9751 | define("moxie/runtime/flash/Runtime", [ |
||
| 9752 | "moxie/core/utils/Basic", |
||
| 9753 | "moxie/core/utils/Env", |
||
| 9754 | "moxie/core/utils/Dom", |
||
| 9755 | "moxie/core/Exceptions", |
||
| 9756 | "moxie/runtime/Runtime" |
||
| 9757 | ], function(Basic, Env, Dom, x, Runtime) { |
||
| 9758 | |||
| 9759 | var type = 'flash', extensions = {}; |
||
| 9760 | |||
| 9761 | /** |
||
| 9762 | Get the version of the Flash Player |
||
| 9763 | |||
| 9764 | @method getShimVersion |
||
| 9765 | @private |
||
| 9766 | @return {Number} Flash Player version |
||
| 9767 | */ |
||
| 9768 | function getShimVersion() { |
||
| 9769 | var version; |
||
| 9770 | |||
| 9771 | try { |
||
| 9772 | version = navigator.plugins['Shockwave Flash']; |
||
| 9773 | version = version.description; |
||
| 9774 | } catch (e1) { |
||
| 9775 | try { |
||
| 9776 | version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); |
||
| 9777 | } catch (e2) { |
||
| 9778 | version = '0.0'; |
||
| 9779 | } |
||
| 9780 | } |
||
| 9781 | version = version.match(/\d+/g); |
||
| 9782 | return parseFloat(version[0] + '.' + version[1]); |
||
| 9783 | } |
||
| 9784 | |||
| 9785 | |||
| 9786 | /** |
||
| 9787 | Cross-browser SWF removal |
||
| 9788 | - Especially needed to safely and completely remove a SWF in Internet Explorer |
||
| 9789 | |||
| 9790 | Originated from SWFObject v2.2 <http://code.google.com/p/swfobject/> |
||
| 9791 | */ |
||
| 9792 | function removeSWF(id) { |
||
| 9793 | var obj = Dom.get(id); |
||
| 9794 | if (obj && obj.nodeName == "OBJECT") { |
||
| 9795 | if (Env.browser === 'IE') { |
||
| 9796 | obj.style.display = "none"; |
||
| 9797 | (function onInit(){ |
||
| 9798 | // http://msdn.microsoft.com/en-us/library/ie/ms534360(v=vs.85).aspx |
||
| 9799 | if (obj.readyState == 4) { |
||
| 9800 | removeObjectInIE(id); |
||
| 9801 | } |
||
| 9802 | else { |
||
| 9803 | setTimeout(onInit, 10); |
||
| 9804 | } |
||
| 9805 | })(); |
||
| 9806 | } |
||
| 9807 | else { |
||
| 9808 | obj.parentNode.removeChild(obj); |
||
| 9809 | } |
||
| 9810 | } |
||
| 9811 | } |
||
| 9812 | |||
| 9813 | |||
| 9814 | function removeObjectInIE(id) { |
||
| 9815 | var obj = Dom.get(id); |
||
| 9816 | if (obj) { |
||
| 9817 | for (var i in obj) { |
||
| 9818 | if (typeof obj[i] == "function") { |
||
| 9819 | obj[i] = null; |
||
| 9820 | } |
||
| 9821 | } |
||
| 9822 | obj.parentNode.removeChild(obj); |
||
| 9823 | } |
||
| 9824 | } |
||
| 9825 | |||
| 9826 | /** |
||
| 9827 | Constructor for the Flash Runtime |
||
| 9828 | */ |
||
| 9829 | function FlashRuntime(options) { |
||
| 9830 | var I = this, initTimer; |
||
| 9831 | |||
| 9832 | options = Basic.extend({ swf_url: Env.swf_url }, options); |
||
| 9833 | |||
| 9834 | Runtime.call(this, options, type, { |
||
| 9835 | access_binary: function(value) { |
||
| 9836 | return value && I.mode === 'browser'; |
||
| 9837 | }, |
||
| 9838 | access_image_binary: function(value) { |
||
| 9839 | return value && I.mode === 'browser'; |
||
| 9840 | }, |
||
| 9841 | display_media: Runtime.capTest(defined('moxie/image/Image')), |
||
| 9842 | do_cors: Runtime.capTrue, |
||
| 9843 | drag_and_drop: false, |
||
| 9844 | report_upload_progress: function() { |
||
| 9845 | return I.mode === 'client'; |
||
| 9846 | }, |
||
| 9847 | resize_image: Runtime.capTrue, |
||
| 9848 | return_response_headers: false, |
||
| 9849 | return_response_type: function(responseType) { |
||
| 9850 | if (responseType === 'json' && !!window.JSON) { |
||
| 9851 | return true; |
||
| 9852 | } |
||
| 9853 | return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser'; |
||
| 9854 | }, |
||
| 9855 | return_status_code: function(code) { |
||
| 9856 | return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]); |
||
| 9857 | }, |
||
| 9858 | select_file: Runtime.capTrue, |
||
| 9859 | select_multiple: Runtime.capTrue, |
||
| 9860 | send_binary_string: function(value) { |
||
| 9861 | return value && I.mode === 'browser'; |
||
| 9862 | }, |
||
| 9863 | send_browser_cookies: function(value) { |
||
| 9864 | return value && I.mode === 'browser'; |
||
| 9865 | }, |
||
| 9866 | send_custom_headers: function(value) { |
||
| 9867 | return value && I.mode === 'browser'; |
||
| 9868 | }, |
||
| 9869 | send_multipart: Runtime.capTrue, |
||
| 9870 | slice_blob: function(value) { |
||
| 9871 | return value && I.mode === 'browser'; |
||
| 9872 | }, |
||
| 9873 | stream_upload: function(value) { |
||
| 9874 | return value && I.mode === 'browser'; |
||
| 9875 | }, |
||
| 9876 | summon_file_dialog: false, |
||
| 9877 | upload_filesize: function(size) { |
||
| 9878 | return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client'; |
||
| 9879 | }, |
||
| 9880 | use_http_method: function(methods) { |
||
| 9881 | return !Basic.arrayDiff(methods, ['GET', 'POST']); |
||
| 9882 | } |
||
| 9883 | }, { |
||
| 9884 | // capabilities that require specific mode |
||
| 9885 | access_binary: function(value) { |
||
| 9886 | return value ? 'browser' : 'client'; |
||
| 9887 | }, |
||
| 9888 | access_image_binary: function(value) { |
||
| 9889 | return value ? 'browser' : 'client'; |
||
| 9890 | }, |
||
| 9891 | report_upload_progress: function(value) { |
||
| 9892 | return value ? 'browser' : 'client'; |
||
| 9893 | }, |
||
| 9894 | return_response_type: function(responseType) { |
||
| 9895 | return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser']; |
||
| 9896 | }, |
||
| 9897 | return_status_code: function(code) { |
||
| 9898 | return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser']; |
||
| 9899 | }, |
||
| 9900 | send_binary_string: function(value) { |
||
| 9901 | return value ? 'browser' : 'client'; |
||
| 9902 | }, |
||
| 9903 | send_browser_cookies: function(value) { |
||
| 9904 | return value ? 'browser' : 'client'; |
||
| 9905 | }, |
||
| 9906 | send_custom_headers: function(value) { |
||
| 9907 | return value ? 'browser' : 'client'; |
||
| 9908 | }, |
||
| 9909 | slice_blob: function(value) { |
||
| 9910 | return value ? 'browser' : 'client'; |
||
| 9911 | }, |
||
| 9912 | stream_upload: function(value) { |
||
| 9913 | return value ? 'client' : 'browser'; |
||
| 9914 | }, |
||
| 9915 | upload_filesize: function(size) { |
||
| 9916 | return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser'; |
||
| 9917 | } |
||
| 9918 | }, 'client'); |
||
| 9919 | |||
| 9920 | |||
| 9921 | // minimal requirement for Flash Player version |
||
| 9922 | if (getShimVersion() < 11.3) { |
||
| 9923 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 9924 | Env.log("\tFlash didn't meet minimal version requirement (11.3)."); |
||
| 9925 | } |
||
| 9926 | |||
| 9927 | this.mode = false; // with falsy mode, runtime won't operable, no matter what the mode was before |
||
| 9928 | } |
||
| 9929 | |||
| 9930 | |||
| 9931 | Basic.extend(this, { |
||
| 9932 | |||
| 9933 | getShim: function() { |
||
| 9934 | return Dom.get(this.uid); |
||
| 9935 | }, |
||
| 9936 | |||
| 9937 | shimExec: function(component, action) { |
||
| 9938 | var args = [].slice.call(arguments, 2); |
||
| 9939 | return I.getShim().exec(this.uid, component, action, args); |
||
| 9940 | }, |
||
| 9941 | |||
| 9942 | init: function() { |
||
| 9943 | var html, el, container; |
||
| 9944 | |||
| 9945 | container = this.getShimContainer(); |
||
| 9946 | |||
| 9947 | // if not the minimal height, shims are not initialized in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) |
||
| 9948 | Basic.extend(container.style, { |
||
| 9949 | position: 'absolute', |
||
| 9950 | top: '-8px', |
||
| 9951 | left: '-8px', |
||
| 9952 | width: '9px', |
||
| 9953 | height: '9px', |
||
| 9954 | overflow: 'hidden' |
||
| 9955 | }); |
||
| 9956 | |||
| 9957 | // insert flash object |
||
| 9958 | html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' + options.swf_url + '" '; |
||
| 9959 | |||
| 9960 | if (Env.browser === 'IE') { |
||
| 9961 | html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '; |
||
| 9962 | } |
||
| 9963 | |||
| 9964 | html += 'width="100%" height="100%" style="outline:0">' + |
||
| 9965 | '<param name="movie" value="' + options.swf_url + '" />' + |
||
| 9966 | '<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Runtime.getGlobalEventTarget() + '" />' + |
||
| 9967 | '<param name="wmode" value="transparent" />' + |
||
| 9968 | '<param name="allowscriptaccess" value="always" />' + |
||
| 9969 | '</object>'; |
||
| 9970 | |||
| 9971 | if (Env.browser === 'IE') { |
||
| 9972 | el = document.createElement('div'); |
||
| 9973 | container.appendChild(el); |
||
| 9974 | el.outerHTML = html; |
||
| 9975 | el = container = null; // just in case |
||
| 9976 | } else { |
||
| 9977 | container.innerHTML = html; |
||
| 9978 | } |
||
| 9979 | |||
| 9980 | // Init is dispatched by the shim |
||
| 9981 | initTimer = setTimeout(function() { |
||
| 9982 | if (I && !I.initialized) { // runtime might be already destroyed by this moment |
||
| 9983 | I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR)); |
||
| 9984 | |||
| 9985 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 9986 | Env.log("\tFlash failed to initialize within a specified period of time (typically 5s)."); |
||
| 9987 | } |
||
| 9988 | } |
||
| 9989 | }, 5000); |
||
| 9990 | }, |
||
| 9991 | |||
| 9992 | destroy: (function(destroy) { // extend default destroy method |
||
| 9993 | return function() { |
||
| 9994 | removeSWF(I.uid); // SWF removal requires special care in IE |
||
| 9995 | |||
| 9996 | destroy.call(I); |
||
| 9997 | clearTimeout(initTimer); // initialization check might be still onwait |
||
| 9998 | options = initTimer = destroy = I = null; |
||
| 9999 | }; |
||
| 10000 | }(this.destroy)) |
||
| 10001 | |||
| 10002 | }, extensions); |
||
| 10003 | } |
||
| 10004 | |||
| 10005 | Runtime.addConstructor(type, FlashRuntime); |
||
| 10006 | |||
| 10007 | return extensions; |
||
| 10008 | }); |
||
| 10009 | |||
| 10010 | // Included from: src/javascript/runtime/flash/file/Blob.js |
||
| 10011 | |||
| 10012 | /** |
||
| 10013 | * Blob.js |
||
| 10014 | * |
||
| 10015 | * Copyright 2013, Moxiecode Systems AB |
||
| 10016 | * Released under GPL License. |
||
| 10017 | * |
||
| 10018 | * License: http://www.plupload.com/license |
||
| 10019 | * Contributing: http://www.plupload.com/contributing |
||
| 10020 | */ |
||
| 10021 | |||
| 10022 | /** |
||
| 10023 | @class moxie/runtime/flash/file/Blob |
||
| 10024 | @private |
||
| 10025 | */ |
||
| 10026 | define("moxie/runtime/flash/file/Blob", [ |
||
| 10027 | "moxie/runtime/flash/Runtime", |
||
| 10028 | "moxie/file/Blob" |
||
| 10029 | ], function(extensions, Blob) { |
||
| 10030 | |||
| 10031 | var FlashBlob = { |
||
| 10032 | slice: function(blob, start, end, type) { |
||
| 10033 | var self = this.getRuntime(); |
||
| 10034 | |||
| 10035 | if (start < 0) { |
||
| 10036 | start = Math.max(blob.size + start, 0); |
||
| 10037 | } else if (start > 0) { |
||
| 10038 | start = Math.min(start, blob.size); |
||
| 10039 | } |
||
| 10040 | |||
| 10041 | if (end < 0) { |
||
| 10042 | end = Math.max(blob.size + end, 0); |
||
| 10043 | } else if (end > 0) { |
||
| 10044 | end = Math.min(end, blob.size); |
||
| 10045 | } |
||
| 10046 | |||
| 10047 | blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || ''); |
||
| 10048 | |||
| 10049 | if (blob) { |
||
| 10050 | blob = new Blob(self.uid, blob); |
||
| 10051 | } |
||
| 10052 | return blob; |
||
| 10053 | } |
||
| 10054 | }; |
||
| 10055 | |||
| 10056 | return (extensions.Blob = FlashBlob); |
||
| 10057 | }); |
||
| 10058 | |||
| 10059 | // Included from: src/javascript/runtime/flash/file/FileInput.js |
||
| 10060 | |||
| 10061 | /** |
||
| 10062 | * FileInput.js |
||
| 10063 | * |
||
| 10064 | * Copyright 2013, Moxiecode Systems AB |
||
| 10065 | * Released under GPL License. |
||
| 10066 | * |
||
| 10067 | * License: http://www.plupload.com/license |
||
| 10068 | * Contributing: http://www.plupload.com/contributing |
||
| 10069 | */ |
||
| 10070 | |||
| 10071 | /** |
||
| 10072 | @class moxie/runtime/flash/file/FileInput |
||
| 10073 | @private |
||
| 10074 | */ |
||
| 10075 | define("moxie/runtime/flash/file/FileInput", [ |
||
| 10076 | "moxie/runtime/flash/Runtime", |
||
| 10077 | "moxie/file/File", |
||
| 10078 | "moxie/core/utils/Dom", |
||
| 10079 | "moxie/core/utils/Basic" |
||
| 10080 | ], function(extensions, File, Dom, Basic) { |
||
| 10081 | |||
| 10082 | var FileInput = { |
||
| 10083 | init: function(options) { |
||
| 10084 | var comp = this, I = this.getRuntime(); |
||
| 10085 | var browseButton = Dom.get(options.browse_button); |
||
| 10086 | |||
| 10087 | if (browseButton) { |
||
| 10088 | browseButton.setAttribute('tabindex', -1); |
||
| 10089 | browseButton = null; |
||
| 10090 | } |
||
| 10091 | |||
| 10092 | this.bind("Change", function() { |
||
| 10093 | var files = I.shimExec.call(comp, 'FileInput', 'getFiles'); |
||
| 10094 | comp.files = []; |
||
| 10095 | Basic.each(files, function(file) { |
||
| 10096 | comp.files.push(new File(I.uid, file)); |
||
| 10097 | }); |
||
| 10098 | }, 999); |
||
| 10099 | |||
| 10100 | this.getRuntime().shimExec.call(this, 'FileInput', 'init', { |
||
| 10101 | accept: options.accept, |
||
| 10102 | multiple: options.multiple |
||
| 10103 | }); |
||
| 10104 | |||
| 10105 | this.trigger('ready'); |
||
| 10106 | } |
||
| 10107 | }; |
||
| 10108 | |||
| 10109 | return (extensions.FileInput = FileInput); |
||
| 10110 | }); |
||
| 10111 | |||
| 10112 | // Included from: src/javascript/runtime/flash/file/FileReader.js |
||
| 10113 | |||
| 10114 | /** |
||
| 10115 | * FileReader.js |
||
| 10116 | * |
||
| 10117 | * Copyright 2013, Moxiecode Systems AB |
||
| 10118 | * Released under GPL License. |
||
| 10119 | * |
||
| 10120 | * License: http://www.plupload.com/license |
||
| 10121 | * Contributing: http://www.plupload.com/contributing |
||
| 10122 | */ |
||
| 10123 | |||
| 10124 | /** |
||
| 10125 | @class moxie/runtime/flash/file/FileReader |
||
| 10126 | @private |
||
| 10127 | */ |
||
| 10128 | define("moxie/runtime/flash/file/FileReader", [ |
||
| 10129 | "moxie/runtime/flash/Runtime", |
||
| 10130 | "moxie/core/utils/Encode" |
||
| 10131 | ], function(extensions, Encode) { |
||
| 10132 | |||
| 10133 | function _formatData(data, op) { |
||
| 10134 | switch (op) { |
||
| 10135 | case 'readAsText': |
||
| 10136 | return Encode.atob(data, 'utf8'); |
||
| 10137 | case 'readAsBinaryString': |
||
| 10138 | return Encode.atob(data); |
||
| 10139 | case 'readAsDataURL': |
||
| 10140 | return data; |
||
| 10141 | } |
||
| 10142 | return null; |
||
| 10143 | } |
||
| 10144 | |||
| 10145 | var FileReader = { |
||
| 10146 | read: function(op, blob) { |
||
| 10147 | var comp = this; |
||
| 10148 | |||
| 10149 | comp.result = ''; |
||
| 10150 | |||
| 10151 | // special prefix for DataURL read mode |
||
| 10152 | if (op === 'readAsDataURL') { |
||
| 10153 | comp.result = 'data:' + (blob.type || '') + ';base64,'; |
||
| 10154 | } |
||
| 10155 | |||
| 10156 | comp.bind('Progress', function(e, data) { |
||
| 10157 | if (data) { |
||
| 10158 | comp.result += _formatData(data, op); |
||
| 10159 | } |
||
| 10160 | }, 999); |
||
| 10161 | |||
| 10162 | return comp.getRuntime().shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid); |
||
| 10163 | } |
||
| 10164 | }; |
||
| 10165 | |||
| 10166 | return (extensions.FileReader = FileReader); |
||
| 10167 | }); |
||
| 10168 | |||
| 10169 | // Included from: src/javascript/runtime/flash/file/FileReaderSync.js |
||
| 10170 | |||
| 10171 | /** |
||
| 10172 | * FileReaderSync.js |
||
| 10173 | * |
||
| 10174 | * Copyright 2013, Moxiecode Systems AB |
||
| 10175 | * Released under GPL License. |
||
| 10176 | * |
||
| 10177 | * License: http://www.plupload.com/license |
||
| 10178 | * Contributing: http://www.plupload.com/contributing |
||
| 10179 | */ |
||
| 10180 | |||
| 10181 | /** |
||
| 10182 | @class moxie/runtime/flash/file/FileReaderSync |
||
| 10183 | @private |
||
| 10184 | */ |
||
| 10185 | define("moxie/runtime/flash/file/FileReaderSync", [ |
||
| 10186 | "moxie/runtime/flash/Runtime", |
||
| 10187 | "moxie/core/utils/Encode" |
||
| 10188 | ], function(extensions, Encode) { |
||
| 10189 | |||
| 10190 | function _formatData(data, op) { |
||
| 10191 | switch (op) { |
||
| 10192 | case 'readAsText': |
||
| 10193 | return Encode.atob(data, 'utf8'); |
||
| 10194 | case 'readAsBinaryString': |
||
| 10195 | return Encode.atob(data); |
||
| 10196 | case 'readAsDataURL': |
||
| 10197 | return data; |
||
| 10198 | } |
||
| 10199 | return null; |
||
| 10200 | } |
||
| 10201 | |||
| 10202 | var FileReaderSync = { |
||
| 10203 | read: function(op, blob) { |
||
| 10204 | var result, self = this.getRuntime(); |
||
| 10205 | |||
| 10206 | result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid); |
||
| 10207 | if (!result) { |
||
| 10208 | return null; // or throw ex |
||
| 10209 | } |
||
| 10210 | |||
| 10211 | // special prefix for DataURL read mode |
||
| 10212 | if (op === 'readAsDataURL') { |
||
| 10213 | result = 'data:' + (blob.type || '') + ';base64,' + result; |
||
| 10214 | } |
||
| 10215 | |||
| 10216 | return _formatData(result, op, blob.type); |
||
| 10217 | } |
||
| 10218 | }; |
||
| 10219 | |||
| 10220 | return (extensions.FileReaderSync = FileReaderSync); |
||
| 10221 | }); |
||
| 10222 | |||
| 10223 | // Included from: src/javascript/runtime/flash/runtime/Transporter.js |
||
| 10224 | |||
| 10225 | /** |
||
| 10226 | * Transporter.js |
||
| 10227 | * |
||
| 10228 | * Copyright 2013, Moxiecode Systems AB |
||
| 10229 | * Released under GPL License. |
||
| 10230 | * |
||
| 10231 | * License: http://www.plupload.com/license |
||
| 10232 | * Contributing: http://www.plupload.com/contributing |
||
| 10233 | */ |
||
| 10234 | |||
| 10235 | /** |
||
| 10236 | @class moxie/runtime/flash/runtime/Transporter |
||
| 10237 | @private |
||
| 10238 | */ |
||
| 10239 | define("moxie/runtime/flash/runtime/Transporter", [ |
||
| 10240 | "moxie/runtime/flash/Runtime", |
||
| 10241 | "moxie/file/Blob" |
||
| 10242 | ], function(extensions, Blob) { |
||
| 10243 | |||
| 10244 | var Transporter = { |
||
| 10245 | getAsBlob: function(type) { |
||
| 10246 | var self = this.getRuntime() |
||
| 10247 | , blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type) |
||
| 10248 | ; |
||
| 10249 | if (blob) { |
||
| 10250 | return new Blob(self.uid, blob); |
||
| 10251 | } |
||
| 10252 | return null; |
||
| 10253 | } |
||
| 10254 | }; |
||
| 10255 | |||
| 10256 | return (extensions.Transporter = Transporter); |
||
| 10257 | }); |
||
| 10258 | |||
| 10259 | // Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js |
||
| 10260 | |||
| 10261 | /** |
||
| 10262 | * XMLHttpRequest.js |
||
| 10263 | * |
||
| 10264 | * Copyright 2013, Moxiecode Systems AB |
||
| 10265 | * Released under GPL License. |
||
| 10266 | * |
||
| 10267 | * License: http://www.plupload.com/license |
||
| 10268 | * Contributing: http://www.plupload.com/contributing |
||
| 10269 | */ |
||
| 10270 | |||
| 10271 | /** |
||
| 10272 | @class moxie/runtime/flash/xhr/XMLHttpRequest |
||
| 10273 | @private |
||
| 10274 | */ |
||
| 10275 | define("moxie/runtime/flash/xhr/XMLHttpRequest", [ |
||
| 10276 | "moxie/runtime/flash/Runtime", |
||
| 10277 | "moxie/core/utils/Basic", |
||
| 10278 | "moxie/file/Blob", |
||
| 10279 | "moxie/file/File", |
||
| 10280 | "moxie/file/FileReaderSync", |
||
| 10281 | "moxie/runtime/flash/file/FileReaderSync", |
||
| 10282 | "moxie/xhr/FormData", |
||
| 10283 | "moxie/runtime/Transporter", |
||
| 10284 | "moxie/runtime/flash/runtime/Transporter" |
||
| 10285 | ], function(extensions, Basic, Blob, File, FileReaderSync, FileReaderSyncFlash, FormData, Transporter, TransporterFlash) { |
||
| 10286 | |||
| 10287 | var XMLHttpRequest = { |
||
| 10288 | |||
| 10289 | send: function(meta, data) { |
||
| 10290 | var target = this, self = target.getRuntime(); |
||
| 10291 | |||
| 10292 | function send() { |
||
| 10293 | meta.transport = self.mode; |
||
| 10294 | self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data); |
||
| 10295 | } |
||
| 10296 | |||
| 10297 | |||
| 10298 | function appendBlob(name, blob) { |
||
| 10299 | self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid); |
||
| 10300 | data = null; |
||
| 10301 | send(); |
||
| 10302 | } |
||
| 10303 | |||
| 10304 | |||
| 10305 | function attachBlob(blob, cb) { |
||
| 10306 | var tr = new Transporter(); |
||
| 10307 | |||
| 10308 | tr.bind("TransportingComplete", function() { |
||
| 10309 | cb(this.result); |
||
| 10310 | }); |
||
| 10311 | |||
| 10312 | tr.transport(blob.getSource(), blob.type, { |
||
| 10313 | ruid: self.uid |
||
| 10314 | }); |
||
| 10315 | } |
||
| 10316 | |||
| 10317 | // copy over the headers if any |
||
| 10318 | if (!Basic.isEmptyObj(meta.headers)) { |
||
| 10319 | Basic.each(meta.headers, function(value, header) { |
||
| 10320 | self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object |
||
| 10321 | }); |
||
| 10322 | } |
||
| 10323 | |||
| 10324 | // transfer over multipart params and blob itself |
||
| 10325 | if (data instanceof FormData) { |
||
| 10326 | var blobField; |
||
| 10327 | data.each(function(value, name) { |
||
| 10328 | if (value instanceof Blob) { |
||
| 10329 | blobField = name; |
||
| 10330 | } else { |
||
| 10331 | self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value); |
||
| 10332 | } |
||
| 10333 | }); |
||
| 10334 | |||
| 10335 | if (!data.hasBlob()) { |
||
| 10336 | data = null; |
||
| 10337 | send(); |
||
| 10338 | } else { |
||
| 10339 | var blob = data.getBlob(); |
||
| 10340 | if (blob.isDetached()) { |
||
| 10341 | attachBlob(blob, function(attachedBlob) { |
||
| 10342 | blob.destroy(); |
||
| 10343 | appendBlob(blobField, attachedBlob); |
||
| 10344 | }); |
||
| 10345 | } else { |
||
| 10346 | appendBlob(blobField, blob); |
||
| 10347 | } |
||
| 10348 | } |
||
| 10349 | } else if (data instanceof Blob) { |
||
| 10350 | if (data.isDetached()) { |
||
| 10351 | attachBlob(data, function(attachedBlob) { |
||
| 10352 | data.destroy(); |
||
| 10353 | data = attachedBlob.uid; |
||
| 10354 | send(); |
||
| 10355 | }); |
||
| 10356 | } else { |
||
| 10357 | data = data.uid; |
||
| 10358 | send(); |
||
| 10359 | } |
||
| 10360 | } else { |
||
| 10361 | send(); |
||
| 10362 | } |
||
| 10363 | }, |
||
| 10364 | |||
| 10365 | getResponse: function(responseType) { |
||
| 10366 | var frs, blob, self = this.getRuntime(); |
||
| 10367 | |||
| 10368 | blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob'); |
||
| 10369 | |||
| 10370 | if (blob) { |
||
| 10371 | blob = new File(self.uid, blob); |
||
| 10372 | |||
| 10373 | if ('blob' === responseType) { |
||
| 10374 | return blob; |
||
| 10375 | } |
||
| 10376 | |||
| 10377 | try { |
||
| 10378 | frs = new FileReaderSync(); |
||
| 10379 | |||
| 10380 | if (!!~Basic.inArray(responseType, ["", "text"])) { |
||
| 10381 | return frs.readAsText(blob); |
||
| 10382 | } else if ('json' === responseType && !!window.JSON) { |
||
| 10383 | return JSON.parse(frs.readAsText(blob)); |
||
| 10384 | } |
||
| 10385 | } finally { |
||
| 10386 | blob.destroy(); |
||
| 10387 | } |
||
| 10388 | } |
||
| 10389 | return null; |
||
| 10390 | }, |
||
| 10391 | |||
| 10392 | abort: function(upload_complete_flag) { |
||
| 10393 | var self = this.getRuntime(); |
||
| 10394 | |||
| 10395 | self.shimExec.call(this, 'XMLHttpRequest', 'abort'); |
||
| 10396 | |||
| 10397 | this.dispatchEvent('readystatechange'); |
||
| 10398 | // this.dispatchEvent('progress'); |
||
| 10399 | this.dispatchEvent('abort'); |
||
| 10400 | |||
| 10401 | //if (!upload_complete_flag) { |
||
| 10402 | // this.dispatchEvent('uploadprogress'); |
||
| 10403 | //} |
||
| 10404 | } |
||
| 10405 | }; |
||
| 10406 | |||
| 10407 | return (extensions.XMLHttpRequest = XMLHttpRequest); |
||
| 10408 | }); |
||
| 10409 | |||
| 10410 | // Included from: src/javascript/runtime/flash/image/Image.js |
||
| 10411 | |||
| 10412 | /** |
||
| 10413 | * Image.js |
||
| 10414 | * |
||
| 10415 | * Copyright 2013, Moxiecode Systems AB |
||
| 10416 | * Released under GPL License. |
||
| 10417 | * |
||
| 10418 | * License: http://www.plupload.com/license |
||
| 10419 | * Contributing: http://www.plupload.com/contributing |
||
| 10420 | */ |
||
| 10421 | |||
| 10422 | /** |
||
| 10423 | @class moxie/runtime/flash/image/Image |
||
| 10424 | @private |
||
| 10425 | */ |
||
| 10426 | define("moxie/runtime/flash/image/Image", [ |
||
| 10427 | "moxie/runtime/flash/Runtime", |
||
| 10428 | "moxie/core/utils/Basic", |
||
| 10429 | "moxie/runtime/Transporter", |
||
| 10430 | "moxie/file/Blob", |
||
| 10431 | "moxie/file/FileReaderSync" |
||
| 10432 | ], function(extensions, Basic, Transporter, Blob, FileReaderSync) { |
||
| 10433 | |||
| 10434 | var Image = { |
||
| 10435 | loadFromBlob: function(blob) { |
||
| 10436 | var comp = this, self = comp.getRuntime(); |
||
| 10437 | |||
| 10438 | function exec(srcBlob) { |
||
| 10439 | self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid); |
||
| 10440 | comp = self = null; |
||
| 10441 | } |
||
| 10442 | |||
| 10443 | if (blob.isDetached()) { // binary string |
||
| 10444 | var tr = new Transporter(); |
||
| 10445 | tr.bind("TransportingComplete", function() { |
||
| 10446 | exec(tr.result.getSource()); |
||
| 10447 | }); |
||
| 10448 | tr.transport(blob.getSource(), blob.type, { ruid: self.uid }); |
||
| 10449 | } else { |
||
| 10450 | exec(blob.getSource()); |
||
| 10451 | } |
||
| 10452 | }, |
||
| 10453 | |||
| 10454 | loadFromImage: function(img) { |
||
| 10455 | var self = this.getRuntime(); |
||
| 10456 | return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid); |
||
| 10457 | }, |
||
| 10458 | |||
| 10459 | getInfo: function() { |
||
| 10460 | var self = this.getRuntime() |
||
| 10461 | , info = self.shimExec.call(this, 'Image', 'getInfo') |
||
| 10462 | ; |
||
| 10463 | |||
| 10464 | if (info.meta && info.meta.thumb && info.meta.thumb.data && !(self.meta.thumb.data instanceof Blob)) { |
||
| 10465 | info.meta.thumb.data = new Blob(self.uid, info.meta.thumb.data); |
||
| 10466 | } |
||
| 10467 | return info; |
||
| 10468 | }, |
||
| 10469 | |||
| 10470 | getAsBlob: function(type, quality) { |
||
| 10471 | var self = this.getRuntime() |
||
| 10472 | , blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality) |
||
| 10473 | ; |
||
| 10474 | if (blob) { |
||
| 10475 | return new Blob(self.uid, blob); |
||
| 10476 | } |
||
| 10477 | return null; |
||
| 10478 | }, |
||
| 10479 | |||
| 10480 | getAsDataURL: function() { |
||
| 10481 | var self = this.getRuntime() |
||
| 10482 | , blob = self.Image.getAsBlob.apply(this, arguments) |
||
| 10483 | , frs |
||
| 10484 | ; |
||
| 10485 | if (!blob) { |
||
| 10486 | return null; |
||
| 10487 | } |
||
| 10488 | frs = new FileReaderSync(); |
||
| 10489 | return frs.readAsDataURL(blob); |
||
| 10490 | } |
||
| 10491 | }; |
||
| 10492 | |||
| 10493 | return (extensions.Image = Image); |
||
| 10494 | }); |
||
| 10495 | |||
| 10496 | // Included from: src/javascript/runtime/silverlight/Runtime.js |
||
| 10497 | |||
| 10498 | /** |
||
| 10499 | * RunTime.js |
||
| 10500 | * |
||
| 10501 | * Copyright 2013, Moxiecode Systems AB |
||
| 10502 | * Released under GPL License. |
||
| 10503 | * |
||
| 10504 | * License: http://www.plupload.com/license |
||
| 10505 | * Contributing: http://www.plupload.com/contributing |
||
| 10506 | */ |
||
| 10507 | |||
| 10508 | /*global ActiveXObject:true */ |
||
| 10509 | |||
| 10510 | /** |
||
| 10511 | Defines constructor for Silverlight runtime. |
||
| 10512 | |||
| 10513 | @class moxie/runtime/silverlight/Runtime |
||
| 10514 | @private |
||
| 10515 | */ |
||
| 10516 | define("moxie/runtime/silverlight/Runtime", [ |
||
| 10517 | "moxie/core/utils/Basic", |
||
| 10518 | "moxie/core/utils/Env", |
||
| 10519 | "moxie/core/utils/Dom", |
||
| 10520 | "moxie/core/Exceptions", |
||
| 10521 | "moxie/runtime/Runtime" |
||
| 10522 | ], function(Basic, Env, Dom, x, Runtime) { |
||
| 10523 | |||
| 10524 | var type = "silverlight", extensions = {}; |
||
| 10525 | |||
| 10526 | function isInstalled(version) { |
||
| 10527 | var isVersionSupported = false, control = null, actualVer, |
||
| 10528 | actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0; |
||
| 10529 | |||
| 10530 | try { |
||
| 10531 | try { |
||
| 10532 | control = new ActiveXObject('AgControl.AgControl'); |
||
| 10533 | |||
| 10534 | if (control.IsVersionSupported(version)) { |
||
| 10535 | isVersionSupported = true; |
||
| 10536 | } |
||
| 10537 | |||
| 10538 | control = null; |
||
| 10539 | } catch (e) { |
||
| 10540 | var plugin = navigator.plugins["Silverlight Plug-In"]; |
||
| 10541 | |||
| 10542 | if (plugin) { |
||
| 10543 | actualVer = plugin.description; |
||
| 10544 | |||
| 10545 | if (actualVer === "1.0.30226.2") { |
||
| 10546 | actualVer = "2.0.30226.2"; |
||
| 10547 | } |
||
| 10548 | |||
| 10549 | actualVerArray = actualVer.split("."); |
||
| 10550 | |||
| 10551 | while (actualVerArray.length > 3) { |
||
| 10552 | actualVerArray.pop(); |
||
| 10553 | } |
||
| 10554 | |||
| 10555 | while ( actualVerArray.length < 4) { |
||
| 10556 | actualVerArray.push(0); |
||
| 10557 | } |
||
| 10558 | |||
| 10559 | reqVerArray = version.split("."); |
||
| 10560 | |||
| 10561 | while (reqVerArray.length > 4) { |
||
| 10562 | reqVerArray.pop(); |
||
| 10563 | } |
||
| 10564 | |||
| 10565 | do { |
||
| 10566 | requiredVersionPart = parseInt(reqVerArray[index], 10); |
||
| 10567 | actualVersionPart = parseInt(actualVerArray[index], 10); |
||
| 10568 | index++; |
||
| 10569 | } while (index < reqVerArray.length && requiredVersionPart === actualVersionPart); |
||
| 10570 | |||
| 10571 | if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) { |
||
| 10572 | isVersionSupported = true; |
||
| 10573 | } |
||
| 10574 | } |
||
| 10575 | } |
||
| 10576 | } catch (e2) { |
||
| 10577 | isVersionSupported = false; |
||
| 10578 | } |
||
| 10579 | |||
| 10580 | return isVersionSupported; |
||
| 10581 | } |
||
| 10582 | |||
| 10583 | /** |
||
| 10584 | Constructor for the Silverlight Runtime |
||
| 10585 | */ |
||
| 10586 | function SilverlightRuntime(options) { |
||
| 10587 | var I = this, initTimer; |
||
| 10588 | |||
| 10589 | options = Basic.extend({ xap_url: Env.xap_url }, options); |
||
| 10590 | |||
| 10591 | Runtime.call(this, options, type, { |
||
| 10592 | access_binary: Runtime.capTrue, |
||
| 10593 | access_image_binary: Runtime.capTrue, |
||
| 10594 | display_media: Runtime.capTest(defined('moxie/image/Image')), |
||
| 10595 | do_cors: Runtime.capTrue, |
||
| 10596 | drag_and_drop: false, |
||
| 10597 | report_upload_progress: Runtime.capTrue, |
||
| 10598 | resize_image: Runtime.capTrue, |
||
| 10599 | return_response_headers: function(value) { |
||
| 10600 | return value && I.mode === 'client'; |
||
| 10601 | }, |
||
| 10602 | return_response_type: function(responseType) { |
||
| 10603 | if (responseType !== 'json') { |
||
| 10604 | return true; |
||
| 10605 | } else { |
||
| 10606 | return !!window.JSON; |
||
| 10607 | } |
||
| 10608 | }, |
||
| 10609 | return_status_code: function(code) { |
||
| 10610 | return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]); |
||
| 10611 | }, |
||
| 10612 | select_file: Runtime.capTrue, |
||
| 10613 | select_multiple: Runtime.capTrue, |
||
| 10614 | send_binary_string: Runtime.capTrue, |
||
| 10615 | send_browser_cookies: function(value) { |
||
| 10616 | return value && I.mode === 'browser'; |
||
| 10617 | }, |
||
| 10618 | send_custom_headers: function(value) { |
||
| 10619 | return value && I.mode === 'client'; |
||
| 10620 | }, |
||
| 10621 | send_multipart: Runtime.capTrue, |
||
| 10622 | slice_blob: Runtime.capTrue, |
||
| 10623 | stream_upload: true, |
||
| 10624 | summon_file_dialog: false, |
||
| 10625 | upload_filesize: Runtime.capTrue, |
||
| 10626 | use_http_method: function(methods) { |
||
| 10627 | return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']); |
||
| 10628 | } |
||
| 10629 | }, { |
||
| 10630 | // capabilities that require specific mode |
||
| 10631 | return_response_headers: function(value) { |
||
| 10632 | return value ? 'client' : 'browser'; |
||
| 10633 | }, |
||
| 10634 | return_status_code: function(code) { |
||
| 10635 | return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser']; |
||
| 10636 | }, |
||
| 10637 | send_browser_cookies: function(value) { |
||
| 10638 | return value ? 'browser' : 'client'; |
||
| 10639 | }, |
||
| 10640 | send_custom_headers: function(value) { |
||
| 10641 | return value ? 'client' : 'browser'; |
||
| 10642 | }, |
||
| 10643 | use_http_method: function(methods) { |
||
| 10644 | return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser']; |
||
| 10645 | } |
||
| 10646 | }); |
||
| 10647 | |||
| 10648 | |||
| 10649 | // minimal requirement |
||
| 10650 | if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') { |
||
| 10651 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 10652 | Env.log("\tSilverlight is not installed or minimal version (2.0.31005.0) requirement not met (not likely)."); |
||
| 10653 | } |
||
| 10654 | |||
| 10655 | this.mode = false; |
||
| 10656 | } |
||
| 10657 | |||
| 10658 | |||
| 10659 | Basic.extend(this, { |
||
| 10660 | getShim: function() { |
||
| 10661 | return Dom.get(this.uid).content.Moxie; |
||
| 10662 | }, |
||
| 10663 | |||
| 10664 | shimExec: function(component, action) { |
||
| 10665 | var args = [].slice.call(arguments, 2); |
||
| 10666 | return I.getShim().exec(this.uid, component, action, args); |
||
| 10667 | }, |
||
| 10668 | |||
| 10669 | init : function() { |
||
| 10670 | var container; |
||
| 10671 | |||
| 10672 | container = this.getShimContainer(); |
||
| 10673 | |||
| 10674 | container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' + |
||
| 10675 | '<param name="source" value="' + options.xap_url + '"/>' + |
||
| 10676 | '<param name="background" value="Transparent"/>' + |
||
| 10677 | '<param name="windowless" value="true"/>' + |
||
| 10678 | '<param name="enablehtmlaccess" value="true"/>' + |
||
| 10679 | '<param name="initParams" value="uid=' + this.uid + ',target=' + Runtime.getGlobalEventTarget() + '"/>' + |
||
| 10680 | '</object>'; |
||
| 10681 | |||
| 10682 | // Init is dispatched by the shim |
||
| 10683 | initTimer = setTimeout(function() { |
||
| 10684 | if (I && !I.initialized) { // runtime might be already destroyed by this moment |
||
| 10685 | I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR)); |
||
| 10686 | |||
| 10687 | if (MXI_DEBUG && Env.debug.runtime) { |
||
| 10688 | Env.log("\Silverlight failed to initialize within a specified period of time (5-10s)."); |
||
| 10689 | } |
||
| 10690 | } |
||
| 10691 | }, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac) |
||
| 10692 | }, |
||
| 10693 | |||
| 10694 | destroy: (function(destroy) { // extend default destroy method |
||
| 10695 | return function() { |
||
| 10696 | destroy.call(I); |
||
| 10697 | clearTimeout(initTimer); // initialization check might be still onwait |
||
| 10698 | options = initTimer = destroy = I = null; |
||
| 10699 | }; |
||
| 10700 | }(this.destroy)) |
||
| 10701 | |||
| 10702 | }, extensions); |
||
| 10703 | } |
||
| 10704 | |||
| 10705 | Runtime.addConstructor(type, SilverlightRuntime); |
||
| 10706 | |||
| 10707 | return extensions; |
||
| 10708 | }); |
||
| 10709 | |||
| 10710 | // Included from: src/javascript/runtime/silverlight/file/Blob.js |
||
| 10711 | |||
| 10712 | /** |
||
| 10713 | * Blob.js |
||
| 10714 | * |
||
| 10715 | * Copyright 2013, Moxiecode Systems AB |
||
| 10716 | * Released under GPL License. |
||
| 10717 | * |
||
| 10718 | * License: http://www.plupload.com/license |
||
| 10719 | * Contributing: http://www.plupload.com/contributing |
||
| 10720 | */ |
||
| 10721 | |||
| 10722 | /** |
||
| 10723 | @class moxie/runtime/silverlight/file/Blob |
||
| 10724 | @private |
||
| 10725 | */ |
||
| 10726 | define("moxie/runtime/silverlight/file/Blob", [ |
||
| 10727 | "moxie/runtime/silverlight/Runtime", |
||
| 10728 | "moxie/core/utils/Basic", |
||
| 10729 | "moxie/runtime/flash/file/Blob" |
||
| 10730 | ], function(extensions, Basic, Blob) { |
||
| 10731 | return (extensions.Blob = Basic.extend({}, Blob)); |
||
| 10732 | }); |
||
| 10733 | |||
| 10734 | // Included from: src/javascript/runtime/silverlight/file/FileInput.js |
||
| 10735 | |||
| 10736 | /** |
||
| 10737 | * FileInput.js |
||
| 10738 | * |
||
| 10739 | * Copyright 2013, Moxiecode Systems AB |
||
| 10740 | * Released under GPL License. |
||
| 10741 | * |
||
| 10742 | * License: http://www.plupload.com/license |
||
| 10743 | * Contributing: http://www.plupload.com/contributing |
||
| 10744 | */ |
||
| 10745 | |||
| 10746 | /** |
||
| 10747 | @class moxie/runtime/silverlight/file/FileInput |
||
| 10748 | @private |
||
| 10749 | */ |
||
| 10750 | define("moxie/runtime/silverlight/file/FileInput", [ |
||
| 10751 | "moxie/runtime/silverlight/Runtime", |
||
| 10752 | "moxie/file/File", |
||
| 10753 | "moxie/core/utils/Dom", |
||
| 10754 | "moxie/core/utils/Basic" |
||
| 10755 | ], function(extensions, File, Dom, Basic) { |
||
| 10756 | |||
| 10757 | function toFilters(accept) { |
||
| 10758 | var filter = ''; |
||
| 10759 | for (var i = 0; i < accept.length; i++) { |
||
| 10760 | filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.'); |
||
| 10761 | } |
||
| 10762 | return filter; |
||
| 10763 | } |
||
| 10764 | |||
| 10765 | |||
| 10766 | var FileInput = { |
||
| 10767 | init: function(options) { |
||
| 10768 | var comp = this, I = this.getRuntime(); |
||
| 10769 | var browseButton = Dom.get(options.browse_button); |
||
| 10770 | |||
| 10771 | if (browseButton) { |
||
| 10772 | browseButton.setAttribute('tabindex', -1); |
||
| 10773 | browseButton = null; |
||
| 10774 | } |
||
| 10775 | |||
| 10776 | this.bind("Change", function() { |
||
| 10777 | var files = I.shimExec.call(comp, 'FileInput', 'getFiles'); |
||
| 10778 | comp.files = []; |
||
| 10779 | Basic.each(files, function(file) { |
||
| 10780 | comp.files.push(new File(I.uid, file)); |
||
| 10781 | }); |
||
| 10782 | }, 999); |
||
| 10783 | |||
| 10784 | I.shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.multiple); |
||
| 10785 | this.trigger('ready'); |
||
| 10786 | }, |
||
| 10787 | |||
| 10788 | setOption: function(name, value) { |
||
| 10789 | if (name == 'accept') { |
||
| 10790 | value = toFilters(value); |
||
| 10791 | } |
||
| 10792 | this.getRuntime().shimExec.call(this, 'FileInput', 'setOption', name, value); |
||
| 10793 | } |
||
| 10794 | }; |
||
| 10795 | |||
| 10796 | return (extensions.FileInput = FileInput); |
||
| 10797 | }); |
||
| 10798 | |||
| 10799 | // Included from: src/javascript/runtime/silverlight/file/FileDrop.js |
||
| 10800 | |||
| 10801 | /** |
||
| 10802 | * FileDrop.js |
||
| 10803 | * |
||
| 10804 | * Copyright 2013, Moxiecode Systems AB |
||
| 10805 | * Released under GPL License. |
||
| 10806 | * |
||
| 10807 | * License: http://www.plupload.com/license |
||
| 10808 | * Contributing: http://www.plupload.com/contributing |
||
| 10809 | */ |
||
| 10810 | |||
| 10811 | /** |
||
| 10812 | @class moxie/runtime/silverlight/file/FileDrop |
||
| 10813 | @private |
||
| 10814 | */ |
||
| 10815 | define("moxie/runtime/silverlight/file/FileDrop", [ |
||
| 10816 | "moxie/runtime/silverlight/Runtime", |
||
| 10817 | "moxie/core/utils/Dom", |
||
| 10818 | "moxie/core/utils/Events" |
||
| 10819 | ], function(extensions, Dom, Events) { |
||
| 10820 | |||
| 10821 | // not exactly useful, since works only in safari (...crickets...) |
||
| 10822 | var FileDrop = { |
||
| 10823 | init: function() { |
||
| 10824 | var comp = this, self = comp.getRuntime(), dropZone; |
||
| 10825 | |||
| 10826 | dropZone = self.getShimContainer(); |
||
| 10827 | |||
| 10828 | Events.addEvent(dropZone, 'dragover', function(e) { |
||
| 10829 | e.preventDefault(); |
||
| 10830 | e.stopPropagation(); |
||
| 10831 | e.dataTransfer.dropEffect = 'copy'; |
||
| 10832 | }, comp.uid); |
||
| 10833 | |||
| 10834 | Events.addEvent(dropZone, 'dragenter', function(e) { |
||
| 10835 | e.preventDefault(); |
||
| 10836 | var flag = Dom.get(self.uid).dragEnter(e); |
||
| 10837 | // If handled, then stop propagation of event in DOM |
||
| 10838 | if (flag) { |
||
| 10839 | e.stopPropagation(); |
||
| 10840 | } |
||
| 10841 | }, comp.uid); |
||
| 10842 | |||
| 10843 | Events.addEvent(dropZone, 'drop', function(e) { |
||
| 10844 | e.preventDefault(); |
||
| 10845 | var flag = Dom.get(self.uid).dragDrop(e); |
||
| 10846 | // If handled, then stop propagation of event in DOM |
||
| 10847 | if (flag) { |
||
| 10848 | e.stopPropagation(); |
||
| 10849 | } |
||
| 10850 | }, comp.uid); |
||
| 10851 | |||
| 10852 | return self.shimExec.call(this, 'FileDrop', 'init'); |
||
| 10853 | } |
||
| 10854 | }; |
||
| 10855 | |||
| 10856 | return (extensions.FileDrop = FileDrop); |
||
| 10857 | }); |
||
| 10858 | |||
| 10859 | // Included from: src/javascript/runtime/silverlight/file/FileReader.js |
||
| 10860 | |||
| 10861 | /** |
||
| 10862 | * FileReader.js |
||
| 10863 | * |
||
| 10864 | * Copyright 2013, Moxiecode Systems AB |
||
| 10865 | * Released under GPL License. |
||
| 10866 | * |
||
| 10867 | * License: http://www.plupload.com/license |
||
| 10868 | * Contributing: http://www.plupload.com/contributing |
||
| 10869 | */ |
||
| 10870 | |||
| 10871 | /** |
||
| 10872 | @class moxie/runtime/silverlight/file/FileReader |
||
| 10873 | @private |
||
| 10874 | */ |
||
| 10875 | define("moxie/runtime/silverlight/file/FileReader", [ |
||
| 10876 | "moxie/runtime/silverlight/Runtime", |
||
| 10877 | "moxie/core/utils/Basic", |
||
| 10878 | "moxie/runtime/flash/file/FileReader" |
||
| 10879 | ], function(extensions, Basic, FileReader) { |
||
| 10880 | return (extensions.FileReader = Basic.extend({}, FileReader)); |
||
| 10881 | }); |
||
| 10882 | |||
| 10883 | // Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js |
||
| 10884 | |||
| 10885 | /** |
||
| 10886 | * FileReaderSync.js |
||
| 10887 | * |
||
| 10888 | * Copyright 2013, Moxiecode Systems AB |
||
| 10889 | * Released under GPL License. |
||
| 10890 | * |
||
| 10891 | * License: http://www.plupload.com/license |
||
| 10892 | * Contributing: http://www.plupload.com/contributing |
||
| 10893 | */ |
||
| 10894 | |||
| 10895 | /** |
||
| 10896 | @class moxie/runtime/silverlight/file/FileReaderSync |
||
| 10897 | @private |
||
| 10898 | */ |
||
| 10899 | define("moxie/runtime/silverlight/file/FileReaderSync", [ |
||
| 10900 | "moxie/runtime/silverlight/Runtime", |
||
| 10901 | "moxie/core/utils/Basic", |
||
| 10902 | "moxie/runtime/flash/file/FileReaderSync" |
||
| 10903 | ], function(extensions, Basic, FileReaderSync) { |
||
| 10904 | return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync)); |
||
| 10905 | }); |
||
| 10906 | |||
| 10907 | // Included from: src/javascript/runtime/silverlight/runtime/Transporter.js |
||
| 10908 | |||
| 10909 | /** |
||
| 10910 | * Transporter.js |
||
| 10911 | * |
||
| 10912 | * Copyright 2013, Moxiecode Systems AB |
||
| 10913 | * Released under GPL License. |
||
| 10914 | * |
||
| 10915 | * License: http://www.plupload.com/license |
||
| 10916 | * Contributing: http://www.plupload.com/contributing |
||
| 10917 | */ |
||
| 10918 | |||
| 10919 | /** |
||
| 10920 | @class moxie/runtime/silverlight/runtime/Transporter |
||
| 10921 | @private |
||
| 10922 | */ |
||
| 10923 | define("moxie/runtime/silverlight/runtime/Transporter", [ |
||
| 10924 | "moxie/runtime/silverlight/Runtime", |
||
| 10925 | "moxie/core/utils/Basic", |
||
| 10926 | "moxie/runtime/flash/runtime/Transporter" |
||
| 10927 | ], function(extensions, Basic, Transporter) { |
||
| 10928 | return (extensions.Transporter = Basic.extend({}, Transporter)); |
||
| 10929 | }); |
||
| 10930 | |||
| 10931 | // Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js |
||
| 10932 | |||
| 10933 | /** |
||
| 10934 | * XMLHttpRequest.js |
||
| 10935 | * |
||
| 10936 | * Copyright 2013, Moxiecode Systems AB |
||
| 10937 | * Released under GPL License. |
||
| 10938 | * |
||
| 10939 | * License: http://www.plupload.com/license |
||
| 10940 | * Contributing: http://www.plupload.com/contributing |
||
| 10941 | */ |
||
| 10942 | |||
| 10943 | /** |
||
| 10944 | @class moxie/runtime/silverlight/xhr/XMLHttpRequest |
||
| 10945 | @private |
||
| 10946 | */ |
||
| 10947 | define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [ |
||
| 10948 | "moxie/runtime/silverlight/Runtime", |
||
| 10949 | "moxie/core/utils/Basic", |
||
| 10950 | "moxie/runtime/flash/xhr/XMLHttpRequest", |
||
| 10951 | "moxie/runtime/silverlight/file/FileReaderSync", |
||
| 10952 | "moxie/runtime/silverlight/runtime/Transporter" |
||
| 10953 | ], function(extensions, Basic, XMLHttpRequest, FileReaderSyncSilverlight, TransporterSilverlight) { |
||
| 10954 | return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest)); |
||
| 10955 | }); |
||
| 10956 | |||
| 10957 | // Included from: src/javascript/runtime/silverlight/image/Image.js |
||
| 10958 | |||
| 10959 | /** |
||
| 10960 | * Image.js |
||
| 10961 | * |
||
| 10962 | * Copyright 2013, Moxiecode Systems AB |
||
| 10963 | * Released under GPL License. |
||
| 10964 | * |
||
| 10965 | * License: http://www.plupload.com/license |
||
| 10966 | * Contributing: http://www.plupload.com/contributing |
||
| 10967 | */ |
||
| 10968 | |||
| 10969 | /** |
||
| 10970 | @class moxie/runtime/silverlight/image/Image |
||
| 10971 | @private |
||
| 10972 | */ |
||
| 10973 | define("moxie/runtime/silverlight/image/Image", [ |
||
| 10974 | "moxie/runtime/silverlight/Runtime", |
||
| 10975 | "moxie/core/utils/Basic", |
||
| 10976 | "moxie/file/Blob", |
||
| 10977 | "moxie/runtime/flash/image/Image" |
||
| 10978 | ], function(extensions, Basic, Blob, Image) { |
||
| 10979 | return (extensions.Image = Basic.extend({}, Image, { |
||
| 10980 | |||
| 10981 | getInfo: function() { |
||
| 10982 | var self = this.getRuntime() |
||
| 10983 | , grps = ['tiff', 'exif', 'gps', 'thumb'] |
||
| 10984 | , info = { meta: {} } |
||
| 10985 | , rawInfo = self.shimExec.call(this, 'Image', 'getInfo') |
||
| 10986 | ; |
||
| 10987 | |||
| 10988 | if (rawInfo.meta) { |
||
| 10989 | Basic.each(grps, function(grp) { |
||
| 10990 | var meta = rawInfo.meta[grp] |
||
| 10991 | , tag |
||
| 10992 | , i |
||
| 10993 | , length |
||
| 10994 | , value |
||
| 10995 | ; |
||
| 10996 | if (meta && meta.keys) { |
||
| 10997 | info.meta[grp] = {}; |
||
| 10998 | for (i = 0, length = meta.keys.length; i < length; i++) { |
||
| 10999 | tag = meta.keys[i]; |
||
| 11000 | value = meta[tag]; |
||
| 11001 | if (value) { |
||
| 11002 | // convert numbers |
||
| 11003 | if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero) |
||
| 11004 | value = parseInt(value, 10); |
||
| 11005 | } else if (/^\d*\.\d+$/.test(value)) { // double |
||
| 11006 | value = parseFloat(value); |
||
| 11007 | } |
||
| 11008 | info.meta[grp][tag] = value; |
||
| 11009 | } |
||
| 11010 | } |
||
| 11011 | } |
||
| 11012 | }); |
||
| 11013 | |||
| 11014 | // save thumb data as blob |
||
| 11015 | if (info.meta && info.meta.thumb && info.meta.thumb.data && !(self.meta.thumb.data instanceof Blob)) { |
||
| 11016 | info.meta.thumb.data = new Blob(self.uid, info.meta.thumb.data); |
||
| 11017 | } |
||
| 11018 | } |
||
| 11019 | |||
| 11020 | info.width = parseInt(rawInfo.width, 10); |
||
| 11021 | info.height = parseInt(rawInfo.height, 10); |
||
| 11022 | info.size = parseInt(rawInfo.size, 10); |
||
| 11023 | info.type = rawInfo.type; |
||
| 11024 | info.name = rawInfo.name; |
||
| 11025 | |||
| 11026 | return info; |
||
| 11027 | }, |
||
| 11028 | |||
| 11029 | resize: function(rect, ratio, opts) { |
||
| 11030 | this.getRuntime().shimExec.call(this, 'Image', 'resize', rect.x, rect.y, rect.width, rect.height, ratio, opts.preserveHeaders, opts.resample); |
||
| 11031 | } |
||
| 11032 | })); |
||
| 11033 | }); |
||
| 11034 | |||
| 11035 | // Included from: src/javascript/runtime/html4/Runtime.js |
||
| 11036 | |||
| 11037 | /** |
||
| 11038 | * Runtime.js |
||
| 11039 | * |
||
| 11040 | * Copyright 2013, Moxiecode Systems AB |
||
| 11041 | * Released under GPL License. |
||
| 11042 | * |
||
| 11043 | * License: http://www.plupload.com/license |
||
| 11044 | * Contributing: http://www.plupload.com/contributing |
||
| 11045 | */ |
||
| 11046 | |||
| 11047 | /*global File:true */ |
||
| 11048 | |||
| 11049 | /** |
||
| 11050 | Defines constructor for HTML4 runtime. |
||
| 11051 | |||
| 11052 | @class moxie/runtime/html4/Runtime |
||
| 11053 | @private |
||
| 11054 | */ |
||
| 11055 | define("moxie/runtime/html4/Runtime", [ |
||
| 11056 | "moxie/core/utils/Basic", |
||
| 11057 | "moxie/core/Exceptions", |
||
| 11058 | "moxie/runtime/Runtime", |
||
| 11059 | "moxie/core/utils/Env" |
||
| 11060 | ], function(Basic, x, Runtime, Env) { |
||
| 11061 | |||
| 11062 | var type = 'html4', extensions = {}; |
||
| 11063 | |||
| 11064 | function Html4Runtime(options) { |
||
| 11065 | var I = this |
||
| 11066 | , Test = Runtime.capTest |
||
| 11067 | , True = Runtime.capTrue |
||
| 11068 | ; |
||
| 11069 | |||
| 11070 | Runtime.call(this, options, type, { |
||
| 11071 | access_binary: Test(window.FileReader || window.File && File.getAsDataURL), |
||
| 11072 | access_image_binary: false, |
||
| 11073 | display_media: Test( |
||
| 11074 | (Env.can('create_canvas') || Env.can('use_data_uri_over32kb')) && |
||
| 11075 | defined('moxie/image/Image') |
||
| 11076 | ), |
||
| 11077 | do_cors: false, |
||
| 11078 | drag_and_drop: false, |
||
| 11079 | filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest |
||
| 11080 | return !( |
||
| 11081 | (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '<')) || |
||
| 11082 | (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) || |
||
| 11083 | (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '<')) || |
||
| 11084 | (Env.browser === 'Firefox' && Env.verComp(Env.version, 37, '<')) |
||
| 11085 | ); |
||
| 11086 | }()), |
||
| 11087 | resize_image: function() { |
||
| 11088 | return extensions.Image && I.can('access_binary') && Env.can('create_canvas'); |
||
| 11089 | }, |
||
| 11090 | report_upload_progress: false, |
||
| 11091 | return_response_headers: false, |
||
| 11092 | return_response_type: function(responseType) { |
||
| 11093 | if (responseType === 'json' && !!window.JSON) { |
||
| 11094 | return true; |
||
| 11095 | } |
||
| 11096 | return !!~Basic.inArray(responseType, ['text', 'document', '']); |
||
| 11097 | }, |
||
| 11098 | return_status_code: function(code) { |
||
| 11099 | return !Basic.arrayDiff(code, [200, 404]); |
||
| 11100 | }, |
||
| 11101 | select_file: function() { |
||
| 11102 | return Env.can('use_fileinput'); |
||
| 11103 | }, |
||
| 11104 | select_multiple: false, |
||
| 11105 | send_binary_string: false, |
||
| 11106 | send_custom_headers: false, |
||
| 11107 | send_multipart: true, |
||
| 11108 | slice_blob: false, |
||
| 11109 | stream_upload: function() { |
||
| 11110 | return I.can('select_file'); |
||
| 11111 | }, |
||
| 11112 | summon_file_dialog: function() { // yeah... some dirty sniffing here... |
||
| 11113 | return I.can('select_file') && !( |
||
| 11114 | (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '<')) || |
||
| 11115 | (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '<')) || |
||
| 11116 | (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) |
||
| 11117 | ); |
||
| 11118 | }, |
||
| 11119 | upload_filesize: True, |
||
| 11120 | use_http_method: function(methods) { |
||
| 11121 | return !Basic.arrayDiff(methods, ['GET', 'POST']); |
||
| 11122 | } |
||
| 11123 | }); |
||
| 11124 | |||
| 11125 | |||
| 11126 | Basic.extend(this, { |
||
| 11127 | init : function() { |
||
| 11128 | this.trigger("Init"); |
||
| 11129 | }, |
||
| 11130 | |||
| 11131 | destroy: (function(destroy) { // extend default destroy method |
||
| 11132 | return function() { |
||
| 11133 | destroy.call(I); |
||
| 11134 | destroy = I = null; |
||
| 11135 | }; |
||
| 11136 | }(this.destroy)) |
||
| 11137 | }); |
||
| 11138 | |||
| 11139 | Basic.extend(this.getShim(), extensions); |
||
| 11140 | } |
||
| 11141 | |||
| 11142 | Runtime.addConstructor(type, Html4Runtime); |
||
| 11143 | |||
| 11144 | return extensions; |
||
| 11145 | }); |
||
| 11146 | |||
| 11147 | // Included from: src/javascript/runtime/html4/file/FileInput.js |
||
| 11148 | |||
| 11149 | /** |
||
| 11150 | * FileInput.js |
||
| 11151 | * |
||
| 11152 | * Copyright 2013, Moxiecode Systems AB |
||
| 11153 | * Released under GPL License. |
||
| 11154 | * |
||
| 11155 | * License: http://www.plupload.com/license |
||
| 11156 | * Contributing: http://www.plupload.com/contributing |
||
| 11157 | */ |
||
| 11158 | |||
| 11159 | /** |
||
| 11160 | @class moxie/runtime/html4/file/FileInput |
||
| 11161 | @private |
||
| 11162 | */ |
||
| 11163 | define("moxie/runtime/html4/file/FileInput", [ |
||
| 11164 | "moxie/runtime/html4/Runtime", |
||
| 11165 | "moxie/file/File", |
||
| 11166 | "moxie/core/utils/Basic", |
||
| 11167 | "moxie/core/utils/Dom", |
||
| 11168 | "moxie/core/utils/Events", |
||
| 11169 | "moxie/core/utils/Mime", |
||
| 11170 | "moxie/core/utils/Env" |
||
| 11171 | ], function(extensions, File, Basic, Dom, Events, Mime, Env) { |
||
| 11172 | |||
| 11173 | function FileInput() { |
||
| 11174 | var _uid, _mimes = [], _options, _browseBtnZIndex; // save original z-index; |
||
| 11175 | |||
| 11176 | function addInput() { |
||
| 11177 | var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid; |
||
| 11178 | |||
| 11179 | uid = Basic.guid('uid_'); |
||
| 11180 | |||
| 11181 | shimContainer = I.getShimContainer(); // we get new ref every time to avoid memory leaks in IE |
||
| 11182 | |||
| 11183 | if (_uid) { // move previous form out of the view |
||
| 11184 | currForm = Dom.get(_uid + '_form'); |
||
| 11185 | if (currForm) { |
||
| 11186 | Basic.extend(currForm.style, { top: '100%' }); |
||
| 11187 | // it shouldn't be possible to tab into the hidden element |
||
| 11188 | currForm.firstChild.setAttribute('tabindex', -1); |
||
| 11189 | } |
||
| 11190 | } |
||
| 11191 | |||
| 11192 | // build form in DOM, since innerHTML version not able to submit file for some reason |
||
| 11193 | form = document.createElement('form'); |
||
| 11194 | form.setAttribute('id', uid + '_form'); |
||
| 11195 | form.setAttribute('method', 'post'); |
||
| 11196 | form.setAttribute('enctype', 'multipart/form-data'); |
||
| 11197 | form.setAttribute('encoding', 'multipart/form-data'); |
||
| 11198 | |||
| 11199 | Basic.extend(form.style, { |
||
| 11200 | overflow: 'hidden', |
||
| 11201 | position: 'absolute', |
||
| 11202 | top: 0, |
||
| 11203 | left: 0, |
||
| 11204 | width: '100%', |
||
| 11205 | height: '100%' |
||
| 11206 | }); |
||
| 11207 | |||
| 11208 | input = document.createElement('input'); |
||
| 11209 | input.setAttribute('id', uid); |
||
| 11210 | input.setAttribute('type', 'file'); |
||
| 11211 | input.setAttribute('accept', _mimes.join(',')); |
||
| 11212 | |||
| 11213 | if (I.can('summon_file_dialog')) { |
||
| 11214 | input.setAttribute('tabindex', -1); |
||
| 11215 | } |
||
| 11216 | |||
| 11217 | Basic.extend(input.style, { |
||
| 11218 | fontSize: '999px', |
||
| 11219 | opacity: 0 |
||
| 11220 | }); |
||
| 11221 | |||
| 11222 | form.appendChild(input); |
||
| 11223 | shimContainer.appendChild(form); |
||
| 11224 | |||
| 11225 | // prepare file input to be placed underneath the browse_button element |
||
| 11226 | Basic.extend(input.style, { |
||
| 11227 | position: 'absolute', |
||
| 11228 | top: 0, |
||
| 11229 | left: 0, |
||
| 11230 | width: '100%', |
||
| 11231 | height: '100%' |
||
| 11232 | }); |
||
| 11233 | |||
| 11234 | if (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) { |
||
| 11235 | Basic.extend(input.style, { |
||
| 11236 | filter : "progid:DXImageTransform.Microsoft.Alpha(opacity=0)" |
||
| 11237 | }); |
||
| 11238 | } |
||
| 11239 | |||
| 11240 | input.onchange = function() { // there should be only one handler for this |
||
| 11241 | var file; |
||
| 11242 | |||
| 11243 | if (!this.value) { |
||
| 11244 | return; |
||
| 11245 | } |
||
| 11246 | |||
| 11247 | if (this.files) { // check if browser is fresh enough |
||
| 11248 | file = this.files[0]; |
||
| 11249 | } else { |
||
| 11250 | file = { |
||
| 11251 | name: this.value |
||
| 11252 | }; |
||
| 11253 | } |
||
| 11254 | |||
| 11255 | file = new File(I.uid, file); |
||
| 11256 | |||
| 11257 | // clear event handler |
||
| 11258 | this.onchange = function() {}; |
||
| 11259 | addInput.call(comp); |
||
| 11260 | |||
| 11261 | comp.files = [file]; |
||
| 11262 | |||
| 11263 | // substitute all ids with file uids (consider file.uid read-only - we cannot do it the other way around) |
||
| 11264 | input.setAttribute('id', file.uid); |
||
| 11265 | form.setAttribute('id', file.uid + '_form'); |
||
| 11266 | |||
| 11267 | comp.trigger('change'); |
||
| 11268 | |||
| 11269 | input = form = null; |
||
| 11270 | }; |
||
| 11271 | |||
| 11272 | |||
| 11273 | // route click event to the input |
||
| 11274 | if (I.can('summon_file_dialog')) { |
||
| 11275 | browseButton = Dom.get(_options.browse_button); |
||
| 11276 | Events.removeEvent(browseButton, 'click', comp.uid); |
||
| 11277 | Events.addEvent(browseButton, 'click', function(e) { |
||
| 11278 | if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file] |
||
| 11279 | input.click(); |
||
| 11280 | } |
||
| 11281 | e.preventDefault(); |
||
| 11282 | }, comp.uid); |
||
| 11283 | } |
||
| 11284 | |||
| 11285 | _uid = uid; |
||
| 11286 | |||
| 11287 | shimContainer = currForm = browseButton = null; |
||
| 11288 | } |
||
| 11289 | |||
| 11290 | Basic.extend(this, { |
||
| 11291 | init: function(options) { |
||
| 11292 | var comp = this, I = comp.getRuntime(), shimContainer; |
||
| 11293 | |||
| 11294 | // figure out accept string |
||
| 11295 | _options = options; |
||
| 11296 | _mimes = Mime.extList2mimes(options.accept, I.can('filter_by_extension')); |
||
| 11297 | |||
| 11298 | shimContainer = I.getShimContainer(); |
||
| 11299 | |||
| 11300 | (function() { |
||
| 11301 | var browseButton, zIndex, top; |
||
| 11302 | |||
| 11303 | browseButton = Dom.get(options.browse_button); |
||
| 11304 | _browseBtnZIndex = Dom.getStyle(browseButton, 'z-index') || 'auto'; |
||
| 11305 | |||
| 11306 | // Route click event to the input[type=file] element for browsers that support such behavior |
||
| 11307 | if (I.can('summon_file_dialog')) { |
||
| 11308 | if (Dom.getStyle(browseButton, 'position') === 'static') { |
||
| 11309 | browseButton.style.position = 'relative'; |
||
| 11310 | } |
||
| 11311 | |||
| 11312 | comp.bind('Refresh', function() { |
||
| 11313 | zIndex = parseInt(_browseBtnZIndex, 10) || 1; |
||
| 11314 | |||
| 11315 | Dom.get(_options.browse_button).style.zIndex = zIndex; |
||
| 11316 | this.getRuntime().getShimContainer().style.zIndex = zIndex - 1; |
||
| 11317 | }); |
||
| 11318 | } else { |
||
| 11319 | // it shouldn't be possible to tab into the hidden element |
||
| 11320 | browseButton.setAttribute('tabindex', -1); |
||
| 11321 | } |
||
| 11322 | |||
| 11323 | /* Since we have to place input[type=file] on top of the browse_button for some browsers, |
||
| 11324 | browse_button loses interactivity, so we restore it here */ |
||
| 11325 | top = I.can('summon_file_dialog') ? browseButton : shimContainer; |
||
| 11326 | |||
| 11327 | Events.addEvent(top, 'mouseover', function() { |
||
| 11328 | comp.trigger('mouseenter'); |
||
| 11329 | }, comp.uid); |
||
| 11330 | |||
| 11331 | Events.addEvent(top, 'mouseout', function() { |
||
| 11332 | comp.trigger('mouseleave'); |
||
| 11333 | }, comp.uid); |
||
| 11334 | |||
| 11335 | Events.addEvent(top, 'mousedown', function() { |
||
| 11336 | comp.trigger('mousedown'); |
||
| 11337 | }, comp.uid); |
||
| 11338 | |||
| 11339 | Events.addEvent(Dom.get(options.container), 'mouseup', function() { |
||
| 11340 | comp.trigger('mouseup'); |
||
| 11341 | }, comp.uid); |
||
| 11342 | |||
| 11343 | browseButton = null; |
||
| 11344 | }()); |
||
| 11345 | |||
| 11346 | addInput.call(this); |
||
| 11347 | |||
| 11348 | shimContainer = null; |
||
| 11349 | |||
| 11350 | // trigger ready event asynchronously |
||
| 11351 | comp.trigger({ |
||
| 11352 | type: 'ready', |
||
| 11353 | async: true |
||
| 11354 | }); |
||
| 11355 | }, |
||
| 11356 | |||
| 11357 | setOption: function(name, value) { |
||
| 11358 | var I = this.getRuntime(); |
||
| 11359 | var input; |
||
| 11360 | |||
| 11361 | if (name == 'accept') { |
||
| 11362 | _mimes = value.mimes || Mime.extList2mimes(value, I.can('filter_by_extension')); |
||
| 11363 | } |
||
| 11364 | |||
| 11365 | // update current input |
||
| 11366 | input = Dom.get(_uid) |
||
| 11367 | if (input) { |
||
| 11368 | input.setAttribute('accept', _mimes.join(',')); |
||
| 11369 | } |
||
| 11370 | }, |
||
| 11371 | |||
| 11372 | |||
| 11373 | disable: function(state) { |
||
| 11374 | var input; |
||
| 11375 | |||
| 11376 | if ((input = Dom.get(_uid))) { |
||
| 11377 | input.disabled = !!state; |
||
| 11378 | } |
||
| 11379 | }, |
||
| 11380 | |||
| 11381 | destroy: function() { |
||
| 11382 | var I = this.getRuntime() |
||
| 11383 | , shim = I.getShim() |
||
| 11384 | , shimContainer = I.getShimContainer() |
||
| 11385 | , container = _options && Dom.get(_options.container) |
||
| 11386 | , browseButton = _options && Dom.get(_options.browse_button) |
||
| 11387 | ; |
||
| 11388 | |||
| 11389 | if (container) { |
||
| 11390 | Events.removeAllEvents(container, this.uid); |
||
| 11391 | } |
||
| 11392 | |||
| 11393 | if (browseButton) { |
||
| 11394 | Events.removeAllEvents(browseButton, this.uid); |
||
| 11395 | browseButton.style.zIndex = _browseBtnZIndex; // reset to original value |
||
| 11396 | } |
||
| 11397 | |||
| 11398 | if (shimContainer) { |
||
| 11399 | Events.removeAllEvents(shimContainer, this.uid); |
||
| 11400 | shimContainer.innerHTML = ''; |
||
| 11401 | } |
||
| 11402 | |||
| 11403 | shim.removeInstance(this.uid); |
||
| 11404 | |||
| 11405 | _uid = _mimes = _options = shimContainer = container = browseButton = shim = null; |
||
| 11406 | } |
||
| 11407 | }); |
||
| 11408 | } |
||
| 11409 | |||
| 11410 | return (extensions.FileInput = FileInput); |
||
| 11411 | }); |
||
| 11412 | |||
| 11413 | // Included from: src/javascript/runtime/html4/file/FileReader.js |
||
| 11414 | |||
| 11415 | /** |
||
| 11416 | * FileReader.js |
||
| 11417 | * |
||
| 11418 | * Copyright 2013, Moxiecode Systems AB |
||
| 11419 | * Released under GPL License. |
||
| 11420 | * |
||
| 11421 | * License: http://www.plupload.com/license |
||
| 11422 | * Contributing: http://www.plupload.com/contributing |
||
| 11423 | */ |
||
| 11424 | |||
| 11425 | /** |
||
| 11426 | @class moxie/runtime/html4/file/FileReader |
||
| 11427 | @private |
||
| 11428 | */ |
||
| 11429 | define("moxie/runtime/html4/file/FileReader", [ |
||
| 11430 | "moxie/runtime/html4/Runtime", |
||
| 11431 | "moxie/runtime/html5/file/FileReader" |
||
| 11432 | ], function(extensions, FileReader) { |
||
| 11433 | return (extensions.FileReader = FileReader); |
||
| 11434 | }); |
||
| 11435 | |||
| 11436 | // Included from: src/javascript/runtime/html4/xhr/XMLHttpRequest.js |
||
| 11437 | |||
| 11438 | /** |
||
| 11439 | * XMLHttpRequest.js |
||
| 11440 | * |
||
| 11441 | * Copyright 2013, Moxiecode Systems AB |
||
| 11442 | * Released under GPL License. |
||
| 11443 | * |
||
| 11444 | * License: http://www.plupload.com/license |
||
| 11445 | * Contributing: http://www.plupload.com/contributing |
||
| 11446 | */ |
||
| 11447 | |||
| 11448 | /** |
||
| 11449 | @class moxie/runtime/html4/xhr/XMLHttpRequest |
||
| 11450 | @private |
||
| 11451 | */ |
||
| 11452 | define("moxie/runtime/html4/xhr/XMLHttpRequest", [ |
||
| 11453 | "moxie/runtime/html4/Runtime", |
||
| 11454 | "moxie/core/utils/Basic", |
||
| 11455 | "moxie/core/utils/Dom", |
||
| 11456 | "moxie/core/utils/Url", |
||
| 11457 | "moxie/core/Exceptions", |
||
| 11458 | "moxie/core/utils/Events", |
||
| 11459 | "moxie/file/Blob", |
||
| 11460 | "moxie/xhr/FormData" |
||
| 11461 | ], function(extensions, Basic, Dom, Url, x, Events, Blob, FormData) { |
||
| 11462 | |||
| 11463 | function XMLHttpRequest() { |
||
| 11464 | var _status, _response, _iframe; |
||
| 11465 | |||
| 11466 | function cleanup(cb) { |
||
| 11467 | var target = this, uid, form, inputs, i, hasFile = false; |
||
| 11468 | |||
| 11469 | if (!_iframe) { |
||
| 11470 | return; |
||
| 11471 | } |
||
| 11472 | |||
| 11473 | uid = _iframe.id.replace(/_iframe$/, ''); |
||
| 11474 | |||
| 11475 | form = Dom.get(uid + '_form'); |
||
| 11476 | if (form) { |
||
| 11477 | inputs = form.getElementsByTagName('input'); |
||
| 11478 | i = inputs.length; |
||
| 11479 | |||
| 11480 | while (i--) { |
||
| 11481 | switch (inputs[i].getAttribute('type')) { |
||
| 11482 | case 'hidden': |
||
| 11483 | inputs[i].parentNode.removeChild(inputs[i]); |
||
| 11484 | break; |
||
| 11485 | case 'file': |
||
| 11486 | hasFile = true; // flag the case for later |
||
| 11487 | break; |
||
| 11488 | } |
||
| 11489 | } |
||
| 11490 | inputs = []; |
||
| 11491 | |||
| 11492 | if (!hasFile) { // we need to keep the form for sake of possible retries |
||
| 11493 | form.parentNode.removeChild(form); |
||
| 11494 | } |
||
| 11495 | form = null; |
||
| 11496 | } |
||
| 11497 | |||
| 11498 | // without timeout, request is marked as canceled (in console) |
||
| 11499 | setTimeout(function() { |
||
| 11500 | Events.removeEvent(_iframe, 'load', target.uid); |
||
| 11501 | if (_iframe.parentNode) { // #382 |
||
| 11502 | _iframe.parentNode.removeChild(_iframe); |
||
| 11503 | } |
||
| 11504 | |||
| 11505 | // check if shim container has any other children, if - not, remove it as well |
||
| 11506 | var shimContainer = target.getRuntime().getShimContainer(); |
||
| 11507 | if (!shimContainer.children.length) { |
||
| 11508 | shimContainer.parentNode.removeChild(shimContainer); |
||
| 11509 | } |
||
| 11510 | |||
| 11511 | shimContainer = _iframe = null; |
||
| 11512 | cb(); |
||
| 11513 | }, 1); |
||
| 11514 | } |
||
| 11515 | |||
| 11516 | Basic.extend(this, { |
||
| 11517 | send: function(meta, data) { |
||
| 11518 | var target = this, I = target.getRuntime(), uid, form, input, blob; |
||
| 11519 | |||
| 11520 | _status = _response = null; |
||
| 11521 | |||
| 11522 | function createIframe() { |
||
| 11523 | var container = I.getShimContainer() || document.body |
||
| 11524 | , temp = document.createElement('div') |
||
| 11525 | ; |
||
| 11526 | |||
| 11527 | // IE 6 won't be able to set the name using setAttribute or iframe.name |
||
| 11528 | temp.innerHTML = '<iframe id="' + uid + '_iframe" name="' + uid + '_iframe" src="javascript:""" style="display:none"></iframe>'; |
||
| 11529 | _iframe = temp.firstChild; |
||
| 11530 | container.appendChild(_iframe); |
||
| 11531 | |||
| 11532 | /* _iframe.onreadystatechange = function() { |
||
| 11533 | console.info(_iframe.readyState); |
||
| 11534 | };*/ |
||
| 11535 | |||
| 11536 | Events.addEvent(_iframe, 'load', function() { // _iframe.onload doesn't work in IE lte 8 |
||
| 11537 | var el; |
||
| 11538 | |||
| 11539 | try { |
||
| 11540 | el = _iframe.contentWindow.document || _iframe.contentDocument || window.frames[_iframe.id].document; |
||
| 11541 | |||
| 11542 | // try to detect some standard error pages |
||
| 11543 | if (/^4(0[0-9]|1[0-7]|2[2346])\s/.test(el.title)) { // test if title starts with 4xx HTTP error |
||
| 11544 | _status = el.title.replace(/^(\d+).*$/, '$1'); |
||
| 11545 | } else { |
||
| 11546 | _status = 200; |
||
| 11547 | // get result |
||
| 11548 | _response = Basic.trim(el.body.innerHTML); |
||
| 11549 | |||
| 11550 | // we need to fire these at least once |
||
| 11551 | target.trigger({ |
||
| 11552 | type: 'progress', |
||
| 11553 | loaded: _response.length, |
||
| 11554 | total: _response.length |
||
| 11555 | }); |
||
| 11556 | |||
| 11557 | if (blob) { // if we were uploading a file |
||
| 11558 | target.trigger({ |
||
| 11559 | type: 'uploadprogress', |
||
| 11560 | loaded: blob.size || 1025, |
||
| 11561 | total: blob.size || 1025 |
||
| 11562 | }); |
||
| 11563 | } |
||
| 11564 | } |
||
| 11565 | } catch (ex) { |
||
| 11566 | if (Url.hasSameOrigin(meta.url)) { |
||
| 11567 | // if response is sent with error code, iframe in IE gets redirected to res://ieframe.dll/http_x.htm |
||
| 11568 | // which obviously results to cross domain error (wtf?) |
||
| 11569 | _status = 404; |
||
| 11570 | } else { |
||
| 11571 | cleanup.call(target, function() { |
||
| 11572 | target.trigger('error'); |
||
| 11573 | }); |
||
| 11574 | return; |
||
| 11575 | } |
||
| 11576 | } |
||
| 11577 | |||
| 11578 | cleanup.call(target, function() { |
||
| 11579 | target.trigger('load'); |
||
| 11580 | }); |
||
| 11581 | }, target.uid); |
||
| 11582 | } // end createIframe |
||
| 11583 | |||
| 11584 | // prepare data to be sent and convert if required |
||
| 11585 | if (data instanceof FormData && data.hasBlob()) { |
||
| 11586 | blob = data.getBlob(); |
||
| 11587 | uid = blob.uid; |
||
| 11588 | input = Dom.get(uid); |
||
| 11589 | form = Dom.get(uid + '_form'); |
||
| 11590 | if (!form) { |
||
| 11591 | throw new x.DOMException(x.DOMException.NOT_FOUND_ERR); |
||
| 11592 | } |
||
| 11593 | } else { |
||
| 11594 | uid = Basic.guid('uid_'); |
||
| 11595 | |||
| 11596 | form = document.createElement('form'); |
||
| 11597 | form.setAttribute('id', uid + '_form'); |
||
| 11598 | form.setAttribute('method', meta.method); |
||
| 11599 | form.setAttribute('enctype', 'multipart/form-data'); |
||
| 11600 | form.setAttribute('encoding', 'multipart/form-data'); |
||
| 11601 | |||
| 11602 | I.getShimContainer().appendChild(form); |
||
| 11603 | } |
||
| 11604 | |||
| 11605 | // set upload target |
||
| 11606 | form.setAttribute('target', uid + '_iframe'); |
||
| 11607 | |||
| 11608 | if (data instanceof FormData) { |
||
| 11609 | data.each(function(value, name) { |
||
| 11610 | if (value instanceof Blob) { |
||
| 11611 | if (input) { |
||
| 11612 | input.setAttribute('name', name); |
||
| 11613 | } |
||
| 11614 | } else { |
||
| 11615 | var hidden = document.createElement('input'); |
||
| 11616 | |||
| 11617 | Basic.extend(hidden, { |
||
| 11618 | type : 'hidden', |
||
| 11619 | name : name, |
||
| 11620 | value : value |
||
| 11621 | }); |
||
| 11622 | |||
| 11623 | // make sure that input[type="file"], if it's there, comes last |
||
| 11624 | if (input) { |
||
| 11625 | form.insertBefore(hidden, input); |
||
| 11626 | } else { |
||
| 11627 | form.appendChild(hidden); |
||
| 11628 | } |
||
| 11629 | } |
||
| 11630 | }); |
||
| 11631 | } |
||
| 11632 | |||
| 11633 | // set destination url |
||
| 11634 | form.setAttribute("action", meta.url); |
||
| 11635 | |||
| 11636 | createIframe(); |
||
| 11637 | form.submit(); |
||
| 11638 | target.trigger('loadstart'); |
||
| 11639 | }, |
||
| 11640 | |||
| 11641 | getStatus: function() { |
||
| 11642 | return _status; |
||
| 11643 | }, |
||
| 11644 | |||
| 11645 | getResponse: function(responseType) { |
||
| 11646 | if ('json' === responseType) { |
||
| 11647 | // strip off <pre>..</pre> tags that might be enclosing the response |
||
| 11648 | if (Basic.typeOf(_response) === 'string' && !!window.JSON) { |
||
| 11649 | try { |
||
| 11650 | return JSON.parse(_response.replace(/^\s*<pre[^>]*>/, '').replace(/<\/pre>\s*$/, '')); |
||
| 11651 | } catch (ex) { |
||
| 11652 | return null; |
||
| 11653 | } |
||
| 11654 | } |
||
| 11655 | } else if ('document' === responseType) { |
||
| 11656 | |||
| 11657 | } |
||
| 11658 | return _response; |
||
| 11659 | }, |
||
| 11660 | |||
| 11661 | abort: function() { |
||
| 11662 | var target = this; |
||
| 11663 | |||
| 11664 | if (_iframe && _iframe.contentWindow) { |
||
| 11665 | if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome |
||
| 11666 | _iframe.contentWindow.stop(); |
||
| 11667 | } else if (_iframe.contentWindow.document.execCommand) { // IE |
||
| 11668 | _iframe.contentWindow.document.execCommand('Stop'); |
||
| 11669 | } else { |
||
| 11670 | _iframe.src = "about:blank"; |
||
| 11671 | } |
||
| 11672 | } |
||
| 11673 | |||
| 11674 | cleanup.call(this, function() { |
||
| 11675 | // target.dispatchEvent('readystatechange'); |
||
| 11676 | target.dispatchEvent('abort'); |
||
| 11677 | }); |
||
| 11678 | }, |
||
| 11679 | |||
| 11680 | destroy: function() { |
||
| 11681 | this.getRuntime().getShim().removeInstance(this.uid); |
||
| 11682 | } |
||
| 11683 | }); |
||
| 11684 | } |
||
| 11685 | |||
| 11686 | return (extensions.XMLHttpRequest = XMLHttpRequest); |
||
| 11687 | }); |
||
| 11688 | |||
| 11689 | // Included from: src/javascript/runtime/html4/image/Image.js |
||
| 11690 | |||
| 11691 | /** |
||
| 11692 | * Image.js |
||
| 11693 | * |
||
| 11694 | * Copyright 2013, Moxiecode Systems AB |
||
| 11695 | * Released under GPL License. |
||
| 11696 | * |
||
| 11697 | * License: http://www.plupload.com/license |
||
| 11698 | * Contributing: http://www.plupload.com/contributing |
||
| 11699 | */ |
||
| 11700 | |||
| 11701 | /** |
||
| 11702 | @class moxie/runtime/html4/image/Image |
||
| 11703 | @private |
||
| 11704 | */ |
||
| 11705 | define("moxie/runtime/html4/image/Image", [ |
||
| 11706 | "moxie/runtime/html4/Runtime", |
||
| 11707 | "moxie/runtime/html5/image/Image" |
||
| 11708 | ], function(extensions, Image) { |
||
| 11709 | return (extensions.Image = Image); |
||
| 11710 | }); |
||
| 11711 | |||
| 11712 | expose(["moxie/core/utils/Basic","moxie/core/utils/Encode","moxie/core/utils/Env","moxie/core/Exceptions","moxie/core/utils/Dom","moxie/core/EventTarget","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/Blob","moxie/core/I18n","moxie/core/utils/Mime","moxie/file/FileInput","moxie/file/File","moxie/file/FileDrop","moxie/file/FileReader","moxie/core/utils/Url","moxie/runtime/RuntimeTarget","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/image/Image","moxie/core/utils/Events","moxie/runtime/html5/image/ResizerCanvas"]); |
||
| 11713 | })(this); |
||
| 11714 | })); |